Browse Source

Merge remote-tracking branch 'up/develop' into designDebugPanel

Conflicts:
	mix/MixClient.cpp
cl-refactor
yann300 9 years ago
parent
commit
023f697341
  1. 1
      CMakeLists.txt
  2. 8
      ethminer/MinerAux.h
  3. 19
      ethvm/CMakeLists.txt
  4. 200
      ethvm/main.cpp
  5. 43
      evmjit/libevmjit/Array.cpp
  6. 27
      evmjit/libevmjit/Cache.cpp
  7. 2
      libdevcore/Common.cpp
  8. 2
      libdevcore/Common.h
  9. 2
      libdevcore/Log.cpp
  10. 3
      libdevcore/vector_ref.h
  11. 192
      libethash-cl/ethash_cl_miner.cpp
  12. 18
      libethash-cl/ethash_cl_miner.h
  13. 4
      libethcore/Common.h
  14. 11
      libethcore/Ethash.cpp
  15. 6
      libethcore/Ethash.h
  16. 73
      libethereum/BlockChain.cpp
  17. 1
      libethereum/BlockChain.h
  18. 208
      libethereum/BlockQueue.cpp
  19. 30
      libethereum/BlockQueue.h
  20. 101
      libethereum/Client.cpp
  21. 2
      libethereum/Client.h
  22. 33
      libethereum/ClientBase.cpp
  23. 5
      libethereum/ClientBase.h
  24. 4
      libethereum/CommonNet.h
  25. 112
      libethereum/EthereumHost.cpp
  26. 3
      libethereum/EthereumHost.h
  27. 6
      libethereum/EthereumPeer.cpp
  28. 36
      libethereum/Executive.cpp
  29. 11
      libethereum/Executive.h
  30. 1
      libethereum/Interface.h
  31. 27
      libethereum/LogFilter.cpp
  32. 15
      libethereum/LogFilter.h
  33. 22
      libethereum/State.cpp
  34. 26
      libevm/ExtVMFace.h
  35. 14
      libevm/VMFace.h
  36. 21
      libevmasm/CommonSubexpressionEliminator.cpp
  37. 2
      libjsqrc/ethereumjs/.jshintrc
  38. 7
      libjsqrc/ethereumjs/.versions
  39. 5
      libjsqrc/ethereumjs/bower.json
  40. 1877
      libjsqrc/ethereumjs/dist/web3-light.js
  41. 71
      libjsqrc/ethereumjs/dist/web3-light.js.map
  42. 4
      libjsqrc/ethereumjs/dist/web3-light.min.js
  43. 1879
      libjsqrc/ethereumjs/dist/web3.js
  44. 71
      libjsqrc/ethereumjs/dist/web3.js.map
  45. 4
      libjsqrc/ethereumjs/dist/web3.min.js
  46. 23
      libjsqrc/ethereumjs/example/contract.html
  47. 29
      libjsqrc/ethereumjs/example/event_inc.html
  48. 203
      libjsqrc/ethereumjs/example/icap.html
  49. 102
      libjsqrc/ethereumjs/example/namereg.html
  50. 76
      libjsqrc/ethereumjs/example/natspec_contract.html
  51. 2
      libjsqrc/ethereumjs/index.js
  52. 6
      libjsqrc/ethereumjs/lib/solidity/param.js
  53. 10
      libjsqrc/ethereumjs/lib/utils/config.js
  54. 39
      libjsqrc/ethereumjs/lib/utils/sha3.js
  55. 85
      libjsqrc/ethereumjs/lib/utils/utils.js
  56. 2
      libjsqrc/ethereumjs/lib/version.json
  57. 13
      libjsqrc/ethereumjs/lib/web3.js
  58. 3
      libjsqrc/ethereumjs/lib/web3/event.js
  59. 31
      libjsqrc/ethereumjs/lib/web3/function.js
  60. 108
      libjsqrc/ethereumjs/lib/web3/icap.js
  61. 46
      libjsqrc/ethereumjs/lib/web3/namereg.js
  62. 94
      libjsqrc/ethereumjs/lib/web3/transfer.js
  63. 2
      libjsqrc/ethereumjs/package.js
  64. 3
      libjsqrc/ethereumjs/package.json
  65. 4
      libjsqrc/ethereumjs/test/batch.js
  66. 2
      libjsqrc/ethereumjs/test/coder.decodeParam.js
  67. 2
      libjsqrc/ethereumjs/test/coder.encodeParam.js
  68. 308
      libjsqrc/ethereumjs/test/contract.js
  69. 17
      libjsqrc/ethereumjs/test/sha3.js
  70. 32
      libjsqrc/ethereumjs/test/utils.isIBAN.js
  71. 8
      libjsqrc/ethereumjs/test/utils.toWei.js
  72. 49
      libjsqrc/ethereumjs/test/web3.eth.sendIBANTransaction.js
  73. 16
      libjsqrc/ethereumjs/test/web3.sha3.js
  74. 3
      libsolidity/AST.cpp
  75. 71
      libsolidity/Compiler.cpp
  76. 27
      libsolidity/CompilerUtils.cpp
  77. 16
      libsolidity/CompilerUtils.h
  78. 310
      libsolidity/ExpressionCompiler.cpp
  79. 25
      libsolidity/ExpressionCompiler.h
  80. 111
      libsolidity/InterfaceHandler.cpp
  81. 48
      libsolidity/InterfaceHandler.h
  82. 3
      libsolidity/Token.h
  83. 4
      libsolidity/Types.cpp
  84. 149
      libweb3jsonrpc/WebThreeStubServerBase.cpp
  85. 7
      libweb3jsonrpc/WebThreeStubServerBase.h
  86. 22
      libweb3jsonrpc/abstractwebthreestubserver.h
  87. 5
      libweb3jsonrpc/spec.json
  88. 9
      mix/MixClient.cpp
  89. 10
      test/TestHelper.cpp
  90. 3
      test/libethereum/StateTestsFiller/stPreCompiledContractsFiller.json
  91. 854
      test/libethereum/StateTestsFiller/stPrecompiledContractsTransactionFiller.json
  92. 5
      test/libethereum/state.cpp
  93. 4
      test/libsolidity/Assembly.cpp
  94. 93
      test/libsolidity/SolidityEndToEndTest.cpp
  95. 21
      test/libsolidity/SolidityNameAndTypeResolution.cpp
  96. 491
      test/libsolidity/SolidityWallet.cpp
  97. 36
      test/libweb3jsonrpc/webthreestubclient.h

1
CMakeLists.txt

@ -421,6 +421,7 @@ if (TOOLS)
add_subdirectory(rlp)
add_subdirectory(abi)
add_subdirectory(ethvm)
add_subdirectory(eth)
if("x${CMAKE_BUILD_TYPE}" STREQUAL "xDebug")

8
ethminer/MinerAux.h

@ -132,10 +132,6 @@ public:
ProofOfWork::GPUMiner::listDevices();
exit(0);
}
else if (arg == "--use-chunks")
{
dagChunks = 4;
}
else if (arg == "--phone-home" && i + 1 < argc)
{
string m = argv[++i];
@ -180,7 +176,7 @@ public:
m_minerType = MinerType::CPU;
else if (arg == "-G" || arg == "--opencl")
{
if (!ProofOfWork::GPUMiner::haveSufficientMemory())
if (!ProofOfWork::GPUMiner::configureGPU())
{
cout << "No GPU device with sufficient memory was found. Defaulting to CPU" << endl;
m_minerType = MinerType::CPU;
@ -273,7 +269,6 @@ public:
ProofOfWork::GPUMiner::setDefaultPlatform(openclPlatform);
ProofOfWork::GPUMiner::setDefaultDevice(openclDevice);
ProofOfWork::GPUMiner::setNumInstances(miningThreads);
ProofOfWork::GPUMiner::setDagChunks(dagChunks);
}
if (mode == OperationMode::DAGInit)
doInitDAG(initDAG);
@ -311,7 +306,6 @@ public:
<< " --opencl-platform <n> When mining using -G/--opencl use OpenCL platform n (default: 0)." << endl
<< " --opencl-device <n> When mining using -G/--opencl use OpenCL device n (default: 0)." << endl
<< " -t, --mining-threads <n> Limit number of CPU/GPU miners to n (default: use everything available on selected platform)" << endl
<< " --use-chunks When using GPU mining upload the DAG to the GPU in 4 chunks. " << endl
;
}

19
ethvm/CMakeLists.txt

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

200
ethvm/main.cpp

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

43
evmjit/libevmjit/Array.cpp

@ -9,8 +9,6 @@
#include "Runtime.h"
#include "Utils.h"
#include <set> // DEBUG only
namespace dev
{
namespace eth
@ -269,52 +267,15 @@ void Array::extend(llvm::Value* _arrayPtr, llvm::Value* _size)
}
}
namespace
{
struct AllocatedMemoryWatchdog
{
std::set<void*> allocatedMemory;
~AllocatedMemoryWatchdog()
{
if (!allocatedMemory.empty())
{
DLOG(mem) << allocatedMemory.size() << " MEM LEAKS!\n";
for (auto&& leak : allocatedMemory)
DLOG(mem) << "\t" << leak << "\n";
}
}
};
AllocatedMemoryWatchdog watchdog;
}
extern "C"
{
using namespace dev::eth::jit;
EXPORT void* ext_realloc(void* _data, size_t _size) noexcept
{
//std::cerr << "REALLOC: " << _data << " [" << _size << "]" << std::endl;
auto newData = std::realloc(_data, _size);
if (_data != newData)
{
DLOG(mem) << "REALLOC: " << newData << " <- " << _data << " [" << _size << "]\n";
watchdog.allocatedMemory.erase(_data);
watchdog.allocatedMemory.insert(newData);
}
return newData;
return std::realloc(_data, _size);
}
EXPORT void ext_free(void* _data) noexcept
{
std::free(_data);
if (_data)
{
DLOG(mem) << "FREE : " << _data << "\n";
watchdog.allocatedMemory.erase(_data);
}
}
} // extern "C"
}

27
evmjit/libevmjit/Cache.cpp

@ -1,5 +1,7 @@
#include "Cache.h"
#include <mutex>
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/Module.h>
#include <llvm/IR/LLVMContext.h>
@ -23,6 +25,8 @@ namespace jit
namespace
{
using Guard = std::lock_guard<std::mutex>;
std::mutex x_cacheMutex;
CacheMode g_mode;
llvm::MemoryBuffer* g_lastObject;
ExecutionEngineListener* g_listener;
@ -43,6 +47,9 @@ namespace
ObjectCache* Cache::getObjectCache(CacheMode _mode, ExecutionEngineListener* _listener)
{
static ObjectCache objectCache;
Guard g{x_cacheMutex};
g_mode = _mode;
g_listener = _listener;
return &objectCache;
@ -50,6 +57,8 @@ ObjectCache* Cache::getObjectCache(CacheMode _mode, ExecutionEngineListener* _li
void Cache::clear()
{
Guard g{x_cacheMutex};
using namespace llvm::sys;
llvm::SmallString<256> cachePath;
path::system_temp_directory(false, cachePath);
@ -62,6 +71,8 @@ void Cache::clear()
void Cache::preload(llvm::ExecutionEngine& _ee, std::unordered_map<std::string, uint64_t>& _funcCache)
{
Guard g{x_cacheMutex};
// TODO: Cache dir should be in one place
using namespace llvm::sys;
llvm::SmallString<256> cachePath;
@ -92,11 +103,14 @@ void Cache::preload(llvm::ExecutionEngine& _ee, std::unordered_map<std::string,
std::unique_ptr<llvm::Module> Cache::getObject(std::string const& id)
{
Guard g{x_cacheMutex};
if (g_mode != CacheMode::on && g_mode != CacheMode::read)
return nullptr;
if (g_listener)
g_listener->stateChanged(ExecState::CacheLoad);
// TODO: Disabled because is not thread-safe.
//if (g_listener)
// g_listener->stateChanged(ExecState::CacheLoad);
DLOG(cache) << id << ": search\n";
if (!CHECK(!g_lastObject))
@ -136,12 +150,15 @@ std::unique_ptr<llvm::Module> Cache::getObject(std::string const& id)
void ObjectCache::notifyObjectCompiled(llvm::Module const* _module, llvm::MemoryBuffer const* _object)
{
Guard g{x_cacheMutex};
// Only in "on" and "write" mode
if (g_mode != CacheMode::on && g_mode != CacheMode::write)
return;
if (g_listener)
g_listener->stateChanged(ExecState::CacheWrite);
// TODO: Disabled because is not thread-safe.
// if (g_listener)
// g_listener->stateChanged(ExecState::CacheWrite);
auto&& id = _module->getModuleIdentifier();
llvm::SmallString<256> cachePath;
@ -161,6 +178,8 @@ void ObjectCache::notifyObjectCompiled(llvm::Module const* _module, llvm::Memory
llvm::MemoryBuffer* ObjectCache::getObject(llvm::Module const* _module)
{
Guard g{x_cacheMutex};
DLOG(cache) << _module->getModuleIdentifier() << ": use\n";
auto o = g_lastObject;
g_lastObject = nullptr;

2
libdevcore/Common.cpp

@ -28,7 +28,7 @@ using namespace dev;
namespace dev
{
char const* Version = "0.9.24";
char const* Version = "0.9.25";
const u256 UndefinedU256 = ~(u256)0;

2
libdevcore/Common.h

@ -181,7 +181,7 @@ private:
/// Scope guard for invariant check in a class derived from HasInvariants.
#if ETH_DEBUG
#define DEV_INVARIANT_CHECK ::dev::InvariantChecker __dev_invariantCheck(this)
#define DEV_INVARIANT_CHECK { ::dev::InvariantChecker __dev_invariantCheck(this); }
#else
#define DEV_INVARIANT_CHECK (void)0;
#endif

2
libdevcore/Log.cpp

@ -40,7 +40,7 @@ mutex x_logOverride;
/// or equal to the currently output verbosity (g_logVerbosity).
static map<type_info const*, bool> s_logOverride;
bool isLogVisible(std::type_info const* _ch, bool _default)
bool dev::isChannelVisible(std::type_info const* _ch, bool _default)
{
Guard l(x_logOverride);
if (s_logOverride.count(_ch))

3
libdevcore/vector_ref.h

@ -43,7 +43,8 @@ public:
vector_ref<_T> cropped(size_t _begin) const { if (m_data && _begin <= m_count) return vector_ref<_T>(m_data + _begin, m_count - _begin); else return vector_ref<_T>(); }
void retarget(_T* _d, size_t _s) { m_data = _d; m_count = _s; }
void retarget(std::vector<_T> const& _t) { m_data = _t.data(); m_count = _t.size(); }
void copyTo(vector_ref<typename std::remove_const<_T>::type> _t) const { memcpy(_t.data(), m_data, std::min(_t.size(), m_count) * sizeof(_T)); }
template <class T> bool overlapsWith(vector_ref<T> _t) const { void const* f1 = data(); void const* t1 = data() + size(); void const* f2 = _t.data(); void const* t2 = _t.data() + _t.size(); return f1 < t2 && t1 > f2; }
void copyTo(vector_ref<typename std::remove_const<_T>::type> _t) const { if (overlapsWith(_t)) memmove(_t.data(), m_data, std::min(_t.size(), m_count) * sizeof(_T)); else memcpy(_t.data(), m_data, std::min(_t.size(), m_count) * sizeof(_T)); }
void populate(vector_ref<typename std::remove_const<_T>::type> _t) const { copyTo(_t); memset(_t.data() + m_count, 0, std::max(_t.size(), m_count) - m_count); }
_T* begin() { return m_data; }

192
libethash-cl/ethash_cl_miner.cpp

@ -52,11 +52,11 @@ using namespace std;
// TODO: If at any point we can use libdevcore in here then we should switch to using a LogChannel
#define ETHCL_LOG(_contents) cout << "[OPENCL]:" << _contents << endl
static void add_definition(std::string& source, char const* id, unsigned value)
static void addDefinition(string& _source, char const* _id, unsigned _value)
{
char buf[256];
sprintf(buf, "#define %s %uu\n", id, value);
source.insert(source.begin(), buf, buf + strlen(buf));
sprintf(buf, "#define %s %uu\n", _id, _value);
_source.insert(_source.begin(), buf, buf + strlen(buf));
}
ethash_cl_miner::search_hook::~search_hook() {}
@ -71,44 +71,44 @@ ethash_cl_miner::~ethash_cl_miner()
finish();
}
std::string ethash_cl_miner::platform_info(unsigned _platformId, unsigned _deviceId)
string ethash_cl_miner::platform_info(unsigned _platformId, unsigned _deviceId)
{
std::vector<cl::Platform> platforms;
vector<cl::Platform> platforms;
cl::Platform::get(&platforms);
if (platforms.empty())
{
ETHCL_LOG("No OpenCL platforms found.");
return std::string();
return string();
}
// get GPU device of the selected platform
std::vector<cl::Device> devices;
unsigned platform_num = std::min<unsigned>(_platformId, platforms.size() - 1);
vector<cl::Device> devices;
unsigned platform_num = min<unsigned>(_platformId, platforms.size() - 1);
platforms[platform_num].getDevices(CL_DEVICE_TYPE_ALL, &devices);
if (devices.empty())
{
ETHCL_LOG("No OpenCL devices found.");
return std::string();
return string();
}
// use selected default device
unsigned device_num = std::min<unsigned>(_deviceId, devices.size() - 1);
unsigned device_num = min<unsigned>(_deviceId, devices.size() - 1);
cl::Device& device = devices[device_num];
std::string device_version = device.getInfo<CL_DEVICE_VERSION>();
string device_version = device.getInfo<CL_DEVICE_VERSION>();
return "{ \"platform\": \"" + platforms[platform_num].getInfo<CL_PLATFORM_NAME>() + "\", \"device\": \"" + device.getInfo<CL_DEVICE_NAME>() + "\", \"version\": \"" + device_version + "\" }";
}
unsigned ethash_cl_miner::get_num_platforms()
unsigned ethash_cl_miner::getNumPlatforms()
{
std::vector<cl::Platform> platforms;
vector<cl::Platform> platforms;
cl::Platform::get(&platforms);
return platforms.size();
}
unsigned ethash_cl_miner::get_num_devices(unsigned _platformId)
unsigned ethash_cl_miner::getNumDevices(unsigned _platformId)
{
std::vector<cl::Platform> platforms;
vector<cl::Platform> platforms;
cl::Platform::get(&platforms);
if (platforms.empty())
{
@ -116,8 +116,8 @@ unsigned ethash_cl_miner::get_num_devices(unsigned _platformId)
return 0;
}
std::vector<cl::Device> devices;
unsigned platform_num = std::min<unsigned>(_platformId, platforms.size() - 1);
vector<cl::Device> devices;
unsigned platform_num = min<unsigned>(_platformId, platforms.size() - 1);
platforms[platform_num].getDevices(CL_DEVICE_TYPE_ALL, &devices);
if (devices.empty())
{
@ -127,9 +127,34 @@ unsigned ethash_cl_miner::get_num_devices(unsigned _platformId)
return devices.size();
}
bool ethash_cl_miner::haveSufficientGPUMemory()
bool ethash_cl_miner::configureGPU()
{
std::vector<cl::Platform> platforms;
return searchForAllDevices([](cl::Device const _device) -> bool
{
cl_ulong result;
_device.getInfo(CL_DEVICE_GLOBAL_MEM_SIZE, &result);
if (result >= ETHASH_CL_MINIMUM_MEMORY)
{
ETHCL_LOG(
"Found suitable OpenCL device [" << _device.getInfo<CL_DEVICE_NAME>()
<< "] with " << result << " bytes of GPU memory"
);
return true;
}
ETHCL_LOG(
"OpenCL device " << _device.getInfo<CL_DEVICE_NAME>()
<< " has insufficient GPU memory." << result <<
" bytes of memory found < " << ETHASH_CL_MINIMUM_MEMORY << " bytes of memory required"
);
return false;
}
);
}
bool ethash_cl_miner::searchForAllDevices(function<bool(cl::Device const&)> _callback)
{
vector<cl::Platform> platforms;
cl::Platform::get(&platforms);
if (platforms.empty())
{
@ -137,50 +162,31 @@ bool ethash_cl_miner::haveSufficientGPUMemory()
return false;
}
for (unsigned i = 0; i < platforms.size(); ++i)
if (haveSufficientGPUMemory(i))
if (searchForAllDevices(i, _callback))
return true;
return false;
}
bool ethash_cl_miner::haveSufficientGPUMemory(unsigned _platformId)
bool ethash_cl_miner::searchForAllDevices(unsigned _platformId, function<bool(cl::Device const&)> _callback)
{
std::vector<cl::Platform> platforms;
vector<cl::Platform> platforms;
cl::Platform::get(&platforms);
if (_platformId >= platforms.size())
return false;
std::vector<cl::Device> devices;
unsigned platform_num = std::min<unsigned>(_platformId, platforms.size() - 1);
platforms[platform_num].getDevices(CL_DEVICE_TYPE_ALL, &devices);
if (devices.empty())
return false;
vector<cl::Device> devices;
platforms[_platformId].getDevices(CL_DEVICE_TYPE_ALL, &devices);
for (cl::Device const& device: devices)
{
cl_ulong result;
device.getInfo(CL_DEVICE_GLOBAL_MEM_SIZE, &result);
if (result >= ETHASH_CL_MINIMUM_MEMORY)
{
ETHCL_LOG(
"Found suitable OpenCL device [" << device.getInfo<CL_DEVICE_NAME>()
<< "] with " << result << " bytes of GPU memory"
);
if (_callback(device))
return true;
}
else
ETHCL_LOG(
"OpenCL device " << device.getInfo<CL_DEVICE_NAME>()
<< " has insufficient GPU memory." << result <<
" bytes of memory found < " << ETHASH_CL_MINIMUM_MEMORY << " bytes of memory required"
);
}
return false;
}
void ethash_cl_miner::listDevices()
void ethash_cl_miner::doForAllDevices(function<void(cl::Device const&)> _callback)
{
std::vector<cl::Platform> platforms;
vector<cl::Platform> platforms;
cl::Platform::get(&platforms);
if (platforms.empty())
{
@ -188,26 +194,32 @@ void ethash_cl_miner::listDevices()
return;
}
for (unsigned i = 0; i < platforms.size(); ++i)
listDevices(i);
doForAllDevices(i, _callback);
}
void ethash_cl_miner::listDevices(unsigned _platformId)
void ethash_cl_miner::doForAllDevices(unsigned _platformId, function<void(cl::Device const&)> _callback)
{
std::vector<cl::Platform> platforms;
vector<cl::Platform> platforms;
cl::Platform::get(&platforms);
if (_platformId >= platforms.size())
return;
std::string outString ="Listing OpenCL devices for platform " + to_string(_platformId) + "\n[deviceID] deviceName\n";
std::vector<cl::Device> devices;
vector<cl::Device> devices;
platforms[_platformId].getDevices(CL_DEVICE_TYPE_ALL, &devices);
unsigned i = 0;
std::string deviceString;
for (cl::Device const& device: devices)
{
outString += "[" + to_string(i) + "] " + device.getInfo<CL_DEVICE_NAME>() + "\n";
++i;
}
_callback(device);
}
void ethash_cl_miner::listDevices()
{
string outString ="\nListing OpenCL devices.\nFORMAT: [deviceID] deviceName\n";
unsigned int i = 0;
doForAllDevices([&outString, &i](cl::Device const _device)
{
outString += "[" + to_string(i) + "] " + _device.getInfo<CL_DEVICE_NAME>() + "\n";
++i;
}
);
ETHCL_LOG(outString);
}
@ -222,19 +234,13 @@ bool ethash_cl_miner::init(
uint64_t _dagSize,
unsigned workgroup_size,
unsigned _platformId,
unsigned _deviceId,
unsigned _dagChunksNum
unsigned _deviceId
)
{
// for now due to the .cl kernels we can only have either 1 big chunk or 4 chunks
assert(_dagChunksNum == 1 || _dagChunksNum == 4);
// now create the number of chunk buffers
m_dagChunksNum = _dagChunksNum;
// get all platforms
try
{
std::vector<cl::Platform> platforms;
vector<cl::Platform> platforms;
cl::Platform::get(&platforms);
if (platforms.empty())
{
@ -243,11 +249,11 @@ bool ethash_cl_miner::init(
}
// use selected platform
_platformId = std::min<unsigned>(_platformId, platforms.size() - 1);
_platformId = min<unsigned>(_platformId, platforms.size() - 1);
ETHCL_LOG("Using platform: " << platforms[_platformId].getInfo<CL_PLATFORM_NAME>().c_str());
// get GPU device of the default platform
std::vector<cl::Device> devices;
vector<cl::Device> devices;
platforms[_platformId].getDevices(CL_DEVICE_TYPE_ALL, &devices);
if (devices.empty())
{
@ -256,10 +262,24 @@ bool ethash_cl_miner::init(
}
// use selected device
cl::Device& device = devices[std::min<unsigned>(_deviceId, devices.size() - 1)];
std::string device_version = device.getInfo<CL_DEVICE_VERSION>();
cl::Device& device = devices[min<unsigned>(_deviceId, devices.size() - 1)];
string device_version = device.getInfo<CL_DEVICE_VERSION>();
ETHCL_LOG("Using device: " << device.getInfo<CL_DEVICE_NAME>().c_str() << "(" << device_version.c_str() << ")");
// configure chunk number depending on max allocateable memory
cl_ulong result;
device.getInfo(CL_DEVICE_MAX_MEM_ALLOC_SIZE, &result);
if (result >= ETHASH_CL_MINIMUM_MEMORY)
{
m_dagChunksNum = 1;
ETHCL_LOG("Using 1 big chunk. Max OpenCL allocateable memory is" << result);
}
else
{
m_dagChunksNum = 4;
ETHCL_LOG("Using 4 chunks. Max OpenCL allocateable memory is" << result);
}
if (strncmp("OpenCL 1.0", device_version.c_str(), 10) == 0)
{
ETHCL_LOG("OpenCL 1.0 is not supported.");
@ -269,7 +289,7 @@ bool ethash_cl_miner::init(
m_opencl_1_1 = true;
// create context
m_context = cl::Context(std::vector<cl::Device>(&device, &device + 1));
m_context = cl::Context(vector<cl::Device>(&device, &device + 1));
m_queue = cl::CommandQueue(m_context, device);
// use requested workgroup size, but we require multiple of 8
@ -278,11 +298,11 @@ bool ethash_cl_miner::init(
// patch source code
// note: ETHASH_CL_MINER_KERNEL is simply ethash_cl_miner_kernel.cl compiled
// into a byte array by bin2h.cmake. There is no need to load the file by hand in runtime
std::string code(ETHASH_CL_MINER_KERNEL, ETHASH_CL_MINER_KERNEL + ETHASH_CL_MINER_KERNEL_SIZE);
add_definition(code, "GROUP_SIZE", m_workgroup_size);
add_definition(code, "DAG_SIZE", (unsigned)(_dagSize / ETHASH_MIX_BYTES));
add_definition(code, "ACCESSES", ETHASH_ACCESSES);
add_definition(code, "MAX_OUTPUTS", c_max_search_results);
string code(ETHASH_CL_MINER_KERNEL, ETHASH_CL_MINER_KERNEL + ETHASH_CL_MINER_KERNEL_SIZE);
addDefinition(code, "GROUP_SIZE", m_workgroup_size);
addDefinition(code, "DAG_SIZE", (unsigned)(_dagSize / ETHASH_MIX_BYTES));
addDefinition(code, "ACCESSES", ETHASH_ACCESSES);
addDefinition(code, "MAX_OUTPUTS", c_max_search_results);
//debugf("%s", code.c_str());
// create miner OpenCL program
@ -301,7 +321,7 @@ bool ethash_cl_miner::init(
ETHCL_LOG(program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(device).c_str());
return false;
}
if (_dagChunksNum == 1)
if (m_dagChunksNum == 1)
{
ETHCL_LOG("Loading single big chunk kernels");
m_hash_kernel = cl::Kernel(program, "ethash_hash");
@ -315,13 +335,13 @@ bool ethash_cl_miner::init(
}
// create buffer for dag
if (_dagChunksNum == 1)
if (m_dagChunksNum == 1)
{
ETHCL_LOG("Creating one big buffer");
m_dagChunks.push_back(cl::Buffer(m_context, CL_MEM_READ_ONLY, _dagSize));
}
else
for (unsigned i = 0; i < _dagChunksNum; i++)
for (unsigned i = 0; i < m_dagChunksNum; i++)
{
// TODO Note: If we ever change to _dagChunksNum other than 4, then the size would need recalculation
ETHCL_LOG("Creating buffer for chunk " << i);
@ -336,7 +356,7 @@ bool ethash_cl_miner::init(
ETHCL_LOG("Creating buffer for header.");
m_header = cl::Buffer(m_context, CL_MEM_READ_ONLY, 32);
if (_dagChunksNum == 1)
if (m_dagChunksNum == 1)
{
ETHCL_LOG("Mapping one big chunk.");
m_queue.enqueueWriteBuffer(m_dagChunks[0], CL_TRUE, 0, _dagSize, _dag);
@ -345,12 +365,12 @@ bool ethash_cl_miner::init(
{
// TODO Note: If we ever change to _dagChunksNum other than 4, then the size would need recalculation
void* dag_ptr[4];
for (unsigned i = 0; i < _dagChunksNum; i++)
for (unsigned i = 0; i < m_dagChunksNum; i++)
{
ETHCL_LOG("Mapping chunk " << i);
dag_ptr[i] = m_queue.enqueueMapBuffer(m_dagChunks[i], true, m_opencl_1_1 ? CL_MAP_WRITE : CL_MAP_WRITE_INVALIDATE_REGION, 0, (i == 3) ? (_dagSize - 3 * ((_dagSize >> 9) << 7)) : (_dagSize >> 9) << 7);
}
for (unsigned i = 0; i < _dagChunksNum; i++)
for (unsigned i = 0; i < m_dagChunksNum; i++)
{
memcpy(dag_ptr[i], (char *)_dag + i*((_dagSize >> 9) << 7), (i == 3) ? (_dagSize - 3 * ((_dagSize >> 9) << 7)) : (_dagSize >> 9) << 7);
m_queue.enqueueUnmapMemObject(m_dagChunks[i], dag_ptr[i]);
@ -382,7 +402,7 @@ void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook
uint64_t start_nonce;
unsigned buf;
};
std::queue<pending_batch> pending;
queue<pending_batch> pending;
static uint32_t const c_zero = 0;
@ -408,8 +428,8 @@ void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook
m_search_kernel.setArg(argPos + 2, ~0u);
unsigned buf = 0;
std::random_device engine;
uint64_t start_nonce = std::uniform_int_distribution<uint64_t>()(engine);
random_device engine;
uint64_t start_nonce = uniform_int_distribution<uint64_t>()(engine);
for (;; start_nonce += c_search_batch_size)
{
// supply output buffer to kernel
@ -432,7 +452,7 @@ void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook
// could use pinned host pointer instead
uint32_t* results = (uint32_t*)m_queue.enqueueMapBuffer(m_search_buf[batch.buf], true, CL_MAP_READ, 0, (1 + c_max_search_results) * sizeof(uint32_t));
unsigned num_found = std::min<unsigned>(results[0], c_max_search_results);
unsigned num_found = min<unsigned>(results[0], c_max_search_results);
uint64_t nonces[c_max_search_results];
for (unsigned i = 0; i != num_found; ++i)

18
libethash-cl/ethash_cl_miner.h

@ -32,21 +32,22 @@ public:
ethash_cl_miner();
~ethash_cl_miner();
static unsigned get_num_platforms();
static unsigned get_num_devices(unsigned _platformId = 0);
static bool searchForAllDevices(unsigned _platformId, std::function<bool(cl::Device const&)> _callback);
static bool searchForAllDevices(std::function<bool(cl::Device const&)> _callback);
static void doForAllDevices(unsigned _platformId, std::function<void(cl::Device const&)> _callback);
static void doForAllDevices(std::function<void(cl::Device const&)> _callback);
static unsigned getNumPlatforms();
static unsigned getNumDevices(unsigned _platformId = 0);
static std::string platform_info(unsigned _platformId = 0, unsigned _deviceId = 0);
static bool haveSufficientGPUMemory();
static bool haveSufficientGPUMemory(unsigned _platformId);
static void listDevices();
static void listDevices(unsigned _platformId);
static bool configureGPU();
bool init(
uint8_t const* _dag,
uint64_t _dagSize,
unsigned workgroup_size = 64,
unsigned _platformId = 0,
unsigned _deviceId = 0,
unsigned _dagChunksNum = 1
unsigned _deviceId = 0
);
void finish();
void search(uint8_t const* header, uint64_t target, search_hook& hook);
@ -61,11 +62,12 @@ private:
cl::CommandQueue m_queue;
cl::Kernel m_hash_kernel;
cl::Kernel m_search_kernel;
unsigned m_dagChunksNum;
unsigned int m_dagChunksNum;
std::vector<cl::Buffer> m_dagChunks;
cl::Buffer m_header;
cl::Buffer m_hash_buf[c_num_buffers];
cl::Buffer m_search_buf[c_num_buffers];
unsigned m_workgroup_size;
bool m_opencl_1_1;
};

4
libethcore/Common.h

@ -85,6 +85,10 @@ using BlockNumber = unsigned;
static const BlockNumber LatestBlock = (BlockNumber)-2;
static const BlockNumber PendingBlock = (BlockNumber)-1;
static const h256 LatestBlockHash = h256(2);
static const h256 EarliestBlockHash = h256(1);
static const h256 PendingBlockHash = h256(0);
enum class RelativeBlock: BlockNumber
{

11
libethcore/Ethash.cpp

@ -285,7 +285,6 @@ private:
unsigned Ethash::GPUMiner::s_platformId = 0;
unsigned Ethash::GPUMiner::s_deviceId = 0;
unsigned Ethash::GPUMiner::s_numInstances = 0;
unsigned Ethash::GPUMiner::s_dagChunks = 1;
Ethash::GPUMiner::GPUMiner(ConstructionInfo const& _ci):
Miner(_ci),
@ -335,7 +334,7 @@ void Ethash::GPUMiner::workLoop()
EthashAux::FullType dag;
while (true)
{
if ((dag = EthashAux::full(w.seedHash, false)))
if ((dag = EthashAux::full(w.seedHash, true)))
break;
if (shouldStop())
{
@ -347,7 +346,7 @@ void Ethash::GPUMiner::workLoop()
this_thread::sleep_for(chrono::milliseconds(500));
}
bytesConstRef dagData = dag->data();
m_miner->init(dagData.data(), dagData.size(), 32, s_platformId, device, s_dagChunks);
m_miner->init(dagData.data(), dagData.size(), 32, s_platformId, device);
}
uint64_t upper64OfBoundary = (uint64_t)(u64)((u256)w.boundary >> 192);
@ -374,7 +373,7 @@ std::string Ethash::GPUMiner::platformInfo()
unsigned Ethash::GPUMiner::getNumDevices()
{
return ethash_cl_miner::get_num_devices(s_platformId);
return ethash_cl_miner::getNumDevices(s_platformId);
}
void Ethash::GPUMiner::listDevices()
@ -382,9 +381,9 @@ void Ethash::GPUMiner::listDevices()
return ethash_cl_miner::listDevices();
}
bool Ethash::GPUMiner::haveSufficientMemory()
bool Ethash::GPUMiner::configureGPU()
{
return ethash_cl_miner::haveSufficientGPUMemory();
return ethash_cl_miner::configureGPU();
}
#endif

6
libethcore/Ethash.h

@ -91,7 +91,7 @@ public:
static void setDagChunks(unsigned) {}
static void setDefaultDevice(unsigned) {}
static void listDevices() {}
static bool haveSufficientMemory() { return false; }
static bool configureGPU() { return false; }
static void setNumInstances(unsigned _instances) { s_numInstances = std::min<unsigned>(_instances, std::thread::hardware_concurrency()); }
protected:
void kickOff() override
@ -120,11 +120,10 @@ public:
static std::string platformInfo();
static unsigned getNumDevices();
static void listDevices();
static bool haveSufficientMemory();
static bool configureGPU();
static void setDefaultPlatform(unsigned _id) { s_platformId = _id; }
static void setDefaultDevice(unsigned _id) { s_deviceId = _id; }
static void setNumInstances(unsigned _instances) { s_numInstances = std::min<unsigned>(_instances, getNumDevices()); }
static void setDagChunks(unsigned _dagChunks) { s_dagChunks = _dagChunks; }
protected:
void kickOff() override;
@ -143,7 +142,6 @@ public:
static unsigned s_platformId;
static unsigned s_deviceId;
static unsigned s_numInstances;
static unsigned s_dagChunks;
};
#else
using GPUMiner = CPUMiner;

73
libethereum/BlockChain.cpp

@ -315,41 +315,44 @@ tuple<h256s, h256s, bool> BlockChain::sync(BlockQueue& _bq, OverlayDB const& _st
h256s fresh;
h256s dead;
h256s badBlocks;
for (auto const& block: blocks)
{
try
{
// Nonce & uncle nonces already verified in verification thread at this point.
ImportRoute r;
DEV_TIMED_ABOVE(Block import, 500)
r = import(block.verified, _stateDB, ImportRequirements::Default & ~ImportRequirements::ValidNonce & ~ImportRequirements::CheckUncles);
fresh += r.first;
dead += r.second;
}
catch (dev::eth::UnknownParent)
{
cwarn << "ODD: Import queue contains block with unknown parent." << LogTag::Error << boost::current_exception_diagnostic_information();
// NOTE: don't reimport since the queue should guarantee everything in the right order.
// Can't continue - chain bad.
for (VerifiedBlock const& block: blocks)
if (!badBlocks.empty())
badBlocks.push_back(block.verified.info.hash());
}
catch (dev::eth::FutureTime)
{
cwarn << "ODD: Import queue contains a block with future time." << LogTag::Error << boost::current_exception_diagnostic_information();
// NOTE: don't reimport since the queue should guarantee everything in the past.
// Can't continue - chain bad.
badBlocks.push_back(block.verified.info.hash());
}
catch (Exception& ex)
else
{
cnote << "Exception while importing block. Someone (Jeff? That you?) seems to be giving us dodgy blocks!" << LogTag::Error << diagnostic_information(ex);
if (m_onBad)
m_onBad(ex);
// NOTE: don't reimport since the queue should guarantee everything in the right order.
// Can't continue - chain bad.
badBlocks.push_back(block.verified.info.hash());
try
{
// Nonce & uncle nonces already verified in verification thread at this point.
ImportRoute r;
DEV_TIMED_ABOVE(Block import, 500)
r = import(block.verified, _stateDB, ImportRequirements::Default & ~ImportRequirements::ValidNonce & ~ImportRequirements::CheckUncles);
fresh += r.first;
dead += r.second;
}
catch (dev::eth::UnknownParent)
{
cwarn << "ODD: Import queue contains block with unknown parent.";// << LogTag::Error << boost::current_exception_diagnostic_information();
// NOTE: don't reimport since the queue should guarantee everything in the right order.
// Can't continue - chain bad.
badBlocks.push_back(block.verified.info.hash());
}
catch (dev::eth::FutureTime)
{
cwarn << "ODD: Import queue contains a block with future time.";// << LogTag::Error << boost::current_exception_diagnostic_information();
// NOTE: don't reimport since the queue should guarantee everything in the past.
// Can't continue - chain bad.
badBlocks.push_back(block.verified.info.hash());
}
catch (Exception& ex)
{
// cnote << "Exception while importing block. Someone (Jeff? That you?) seems to be giving us dodgy blocks!";// << LogTag::Error << diagnostic_information(ex);
if (m_onBad)
m_onBad(ex);
// NOTE: don't reimport since the queue should guarantee everything in the right order.
// Can't continue - chain bad.
badBlocks.push_back(block.verified.info.hash());
}
}
}
return make_tuple(fresh, dead, _bq.doneDrain(badBlocks));
}
@ -393,7 +396,7 @@ ImportRoute BlockChain::import(bytes const& _block, OverlayDB const& _db, Import
#if ETH_CATCH
catch (Exception& ex)
{
clog(BlockChainNote) << " Malformed block: " << diagnostic_information(ex);
// clog(BlockChainNote) << " Malformed block: " << diagnostic_information(ex);
ex << errinfo_now(time(0));
ex << errinfo_block(_block);
throw;
@ -923,8 +926,8 @@ void BlockChain::checkConsistency()
delete it;
}
static inline unsigned upow(unsigned a, unsigned b) { while (b-- > 0) a *= a; return a; }
static inline unsigned ceilDiv(unsigned n, unsigned d) { return n / (n + d - 1); }
static inline unsigned upow(unsigned a, unsigned b) { if (!b) return 1; while (--b > 0) a *= a; return a; }
static inline unsigned ceilDiv(unsigned n, unsigned d) { return (n + d - 1) / d; }
//static inline unsigned floorDivPow(unsigned n, unsigned a, unsigned b) { return n / upow(a, b); }
//static inline unsigned ceilDivPow(unsigned n, unsigned a, unsigned b) { return ceilDiv(n, upow(a, b)); }

1
libethereum/BlockChain.h

@ -144,6 +144,7 @@ public:
BlockLogBlooms logBlooms() const { return logBlooms(currentHash()); }
/// Get the transactions' receipts of a block (or the most recent mined if none given). Thread-safe.
/// receipts are given in the same order are in the same order as the transactions
BlockReceipts receipts(h256 const& _hash) const { return queryExtras<BlockReceipts, ExtraReceipts>(_hash, m_receipts, x_receipts, NullBlockReceipts); }
BlockReceipts receipts() const { return receipts(currentHash()); }

208
libethereum/BlockQueue.cpp

@ -37,8 +37,16 @@ const char* BlockQueueChannel::name() { return EthOrange "[]>"; }
const char* BlockQueueChannel::name() { return EthOrange "▣┅▶"; }
#endif
BlockQueue::BlockQueue()
size_t const c_maxKnownCount = 100000;
size_t const c_maxKnownSize = 128 * 1024 * 1024;
size_t const c_maxUnknownCount = 100000;
size_t const c_maxUnknownSize = 512 * 1024 * 1024; // Block size can be ~50kb
BlockQueue::BlockQueue():
m_unknownSize(0),
m_knownSize(0),
m_unknownCount(0),
m_knownCount(0)
{
// Allow some room for other activity
unsigned verifierThreads = std::max(thread::hardware_concurrency(), 3U) - 2U;
@ -57,11 +65,29 @@ BlockQueue::~BlockQueue()
i.join();
}
void BlockQueue::clear()
{
WriteGuard l(m_lock);
DEV_INVARIANT_CHECK;
Guard l2(m_verification);
m_readySet.clear();
m_drainingSet.clear();
m_verified.clear();
m_unverified.clear();
m_unknownSet.clear();
m_unknown.clear();
m_future.clear();
m_unknownSize = 0;
m_unknownCount = 0;
m_knownSize = 0;
m_knownCount = 0;
}
void BlockQueue::verifierBody()
{
while (!m_deleting)
{
std::pair<h256, bytes> work;
UnverifiedBlock work;
{
unique_lock<Mutex> l(m_verification);
@ -71,12 +97,13 @@ void BlockQueue::verifierBody()
swap(work, m_unverified.front());
m_unverified.pop_front();
BlockInfo bi;
bi.mixHash = work.first;
bi.mixHash = work.hash;
bi.parentHash = work.parentHash;
m_verifying.push_back(VerifiedBlock { VerifiedBlockRef { bytesConstRef(), move(bi), Transactions() }, bytes() });
}
VerifiedBlock res;
swap(work.second, res.blockData);
swap(work.block, res.blockData);
try
{
res.verified = BlockChain::verifyBlock(res.blockData, m_onBad);
@ -88,33 +115,46 @@ void BlockQueue::verifierBody()
// has to be this order as that's how invariants() assumes.
WriteGuard l2(m_lock);
unique_lock<Mutex> l(m_verification);
m_readySet.erase(work.first);
m_knownBad.insert(work.first);
m_readySet.erase(work.hash);
m_knownBad.insert(work.hash);
}
unique_lock<Mutex> l(m_verification);
for (auto it = m_verifying.begin(); it != m_verifying.end(); ++it)
if (it->verified.info.mixHash == work.first)
if (it->verified.info.mixHash == work.hash)
{
m_verifying.erase(it);
goto OK1;
}
cwarn << "GAA BlockQueue corrupt: job cancelled but cannot be found in m_verifying queue.";
cwarn << "BlockQueue missing our job: was there a GM?";
OK1:;
continue;
}
bool ready = false;
{
WriteGuard l2(m_lock);
unique_lock<Mutex> l(m_verification);
if (m_verifying.front().verified.info.mixHash == work.first)
if (!m_verifying.empty() && m_verifying.front().verified.info.mixHash == work.hash)
{
// we're next!
m_verifying.pop_front();
m_verified.push_back(move(res));
if (m_knownBad.count(res.verified.info.parentHash))
{
m_readySet.erase(res.verified.info.hash());
m_knownBad.insert(res.verified.info.hash());
}
else
m_verified.push_back(move(res));
while (m_verifying.size() && !m_verifying.front().blockData.empty())
{
m_verified.push_back(move(m_verifying.front()));
if (m_knownBad.count(m_verifying.front().verified.info.parentHash))
{
m_readySet.erase(m_verifying.front().verified.info.hash());
m_knownBad.insert(res.verified.info.hash());
}
else
m_verified.push_back(move(m_verifying.front()));
m_verifying.pop_front();
}
ready = true;
@ -122,12 +162,12 @@ void BlockQueue::verifierBody()
else
{
for (auto& i: m_verifying)
if (i.verified.info.mixHash == work.first)
if (i.verified.info.mixHash == work.hash)
{
i = move(res);
goto OK;
}
cwarn << "GAA BlockQueue corrupt: job finished but cannot be found in m_verifying queue.";
cwarn << "BlockQueue missing our job: was there a GM?";
OK:;
}
}
@ -187,6 +227,8 @@ ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc, boo
if (strftime(buf, 24, "%X", localtime(&bit)) == 0)
buf[0] = '\0'; // empty if case strftime fails
cblockq << "OK - queued for future [" << bi.timestamp << "vs" << time(0) << "] - will wait until" << buf;
m_unknownSize += _block.size();
m_unknownCount++;
return ImportResult::FutureTime;
}
else
@ -195,6 +237,7 @@ ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc, boo
if (m_knownBad.count(bi.parentHash))
{
m_knownBad.insert(bi.hash());
updateBad(bi.hash());
// bad parent; this is bad too, note it as such
return ImportResult::BadChain;
}
@ -204,6 +247,8 @@ ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc, boo
cblockq << "OK - queued as unknown parent:" << bi.parentHash;
m_unknown.insert(make_pair(bi.parentHash, make_pair(h, _block.toBytes())));
m_unknownSet.insert(h);
m_unknownSize += _block.size();
m_unknownCount++;
return ImportResult::UnknownParent;
}
@ -212,9 +257,11 @@ ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc, boo
// If valid, append to blocks.
cblockq << "OK - ready for chain insertion.";
DEV_GUARDED(m_verification)
m_unverified.push_back(make_pair(h, _block.toBytes()));
m_unverified.push_back(UnverifiedBlock { h, bi.parentHash, _block.toBytes() });
m_moreToVerify.notify_one();
m_readySet.insert(h);
m_knownSize += _block.size();
m_knownCount++;
noteReady_WITH_LOCK(h);
@ -223,30 +270,93 @@ ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc, boo
}
}
bool BlockQueue::doneDrain(h256s const& _bad)
void BlockQueue::updateBad(h256 const& _bad)
{
WriteGuard l(m_lock);
DEV_INVARIANT_CHECK;
m_drainingSet.clear();
if (_bad.size())
DEV_GUARDED(m_verification)
{
vector<VerifiedBlock> old;
DEV_GUARDED(m_verification)
swap(m_verified, old);
for (auto& b: old)
collectUnknownBad(_bad);
bool moreBad = true;
while (moreBad)
{
if (m_knownBad.count(b.verified.info.parentHash))
{
m_knownBad.insert(b.verified.info.hash());
m_readySet.erase(b.verified.info.hash());
}
else
DEV_GUARDED(m_verification)
moreBad = false;
std::vector<VerifiedBlock> oldVerified;
swap(m_verified, oldVerified);
for (auto& b: oldVerified)
if (m_knownBad.count(b.verified.info.parentHash) || m_knownBad.count(b.verified.info.hash()))
{
m_knownBad.insert(b.verified.info.hash());
m_readySet.erase(b.verified.info.hash());
collectUnknownBad(b.verified.info.hash());
moreBad = true;
}
else
m_verified.push_back(std::move(b));
std::deque<UnverifiedBlock> oldUnverified;
swap(m_unverified, oldUnverified);
for (auto& b: oldUnverified)
if (m_knownBad.count(b.parentHash) || m_knownBad.count(b.hash))
{
m_knownBad.insert(b.hash);
m_readySet.erase(b.hash);
collectUnknownBad(b.hash);
moreBad = true;
}
else
m_unverified.push_back(std::move(b));
std::deque<VerifiedBlock> oldVerifying;
swap(m_verifying, oldVerifying);
for (auto& b: oldVerifying)
if (m_knownBad.count(b.verified.info.parentHash) || m_knownBad.count(b.verified.info.mixHash))
{
h256 const& h = b.blockData.size() != 0 ? b.verified.info.hash() : b.verified.info.mixHash;
m_knownBad.insert(h);
m_readySet.erase(h);
collectUnknownBad(h);
moreBad = true;
}
else
m_verifying.push_back(std::move(b));
}
}
m_knownBad += _bad;
return !m_readySet.empty();
DEV_INVARIANT_CHECK;
}
void BlockQueue::collectUnknownBad(h256 const& _bad)
{
list<h256> badQueue(1, _bad);
while (!badQueue.empty())
{
auto r = m_unknown.equal_range(badQueue.front());
badQueue.pop_front();
for (auto it = r.first; it != r.second; ++it)
{
m_unknownSize -= it->second.second.size();
m_unknownCount--;
auto newBad = it->second.first;
m_unknownSet.erase(newBad);
m_knownBad.insert(newBad);
badQueue.push_back(newBad);
}
m_unknown.erase(r.first, r.second);
}
}
bool BlockQueue::doneDrain(h256s const& _bad)
{
WriteGuard l(m_lock);
DEV_INVARIANT_CHECK;
m_drainingSet.clear();
if (_bad.size())
{
// at least one of them was bad.
m_knownBad += _bad;
for (h256 const& b : _bad)
updateBad(b);
} return !m_readySet.empty();
}
void BlockQueue::tick(BlockChain const& _bc)
@ -270,7 +380,11 @@ void BlockQueue::tick(BlockChain const& _bc)
DEV_INVARIANT_CHECK;
auto end = m_future.lower_bound(t);
for (auto i = m_future.begin(); i != end; ++i)
{
m_unknownSize -= i->second.second.size();
m_unknownCount--;
todo.push_back(move(i->second));
}
m_future.erase(m_future.begin(), end);
}
}
@ -301,12 +415,24 @@ QueueStatus BlockQueue::blockStatus(h256 const& _h) const
QueueStatus::Unknown;
}
bool BlockQueue::knownFull() const
{
return m_knownSize > c_maxKnownSize || m_knownCount > c_maxKnownCount;
}
bool BlockQueue::unknownFull() const
{
return m_unknownSize > c_maxUnknownSize || m_unknownCount > c_maxUnknownCount;
}
void BlockQueue::drain(VerifiedBlocks& o_out, unsigned _max)
{
WriteGuard l(m_lock);
DEV_INVARIANT_CHECK;
if (m_drainingSet.empty())
{
bool wasFull = knownFull();
DEV_GUARDED(m_verification)
{
o_out.resize(min<unsigned>(_max, m_verified.size()));
@ -320,8 +446,13 @@ void BlockQueue::drain(VerifiedBlocks& o_out, unsigned _max)
auto h = bs.verified.info.hash();
m_drainingSet.insert(h);
m_readySet.erase(h);
m_knownSize -= bs.verified.block.size();
m_knownCount--;
}
if (wasFull && !knownFull())
m_onRoomAvailable();
}
}
bool BlockQueue::invariants() const
@ -342,7 +473,11 @@ void BlockQueue::noteReady_WITH_LOCK(h256 const& _good)
for (auto it = r.first; it != r.second; ++it)
{
DEV_GUARDED(m_verification)
m_unverified.push_back(it->second);
m_unverified.push_back(UnverifiedBlock { it->second.first, it->first, it->second.second });
m_knownSize += it->second.second.size();
m_knownCount++;
m_unknownSize -= it->second.second.size();
m_unknownCount--;
auto newReady = it->second.first;
m_unknownSet.erase(newReady);
m_readySet.insert(newReady);
@ -353,6 +488,7 @@ void BlockQueue::noteReady_WITH_LOCK(h256 const& _good)
}
if (notify)
m_moreToVerify.notify_all();
DEV_INVARIANT_CHECK;
}
void BlockQueue::retryAllUnknown()
@ -362,13 +498,17 @@ void BlockQueue::retryAllUnknown()
for (auto it = m_unknown.begin(); it != m_unknown.end(); ++it)
{
DEV_GUARDED(m_verification)
m_unverified.push_back(it->second);
m_unverified.push_back(UnverifiedBlock { it->second.first, it->first, it->second.second });
auto newReady = it->second.first;
m_unknownSet.erase(newReady);
m_readySet.insert(newReady);
m_knownCount++;
m_moreToVerify.notify_one();
}
m_unknown.clear();
m_knownSize += m_unknownSize;
m_unknownSize = 0;
m_unknownCount = 0;
m_moreToVerify.notify_all();
}

30
libethereum/BlockQueue.h

@ -76,7 +76,7 @@ public:
~BlockQueue();
/// Import a block into the queue.
ImportResult import(bytesConstRef _tx, BlockChain const& _bc, bool _isOurs = false);
ImportResult import(bytesConstRef _block, BlockChain const& _bc, bool _isOurs = false);
/// Notes that time has moved on and some blocks that used to be "in the future" may no be valid.
void tick(BlockChain const& _bc);
@ -99,7 +99,7 @@ public:
std::pair<unsigned, unsigned> items() const { ReadGuard l(m_lock); return std::make_pair(m_readySet.size(), m_unknownSet.size()); }
/// Clear everything.
void clear() { WriteGuard l(m_lock); DEV_INVARIANT_CHECK; Guard l2(m_verification); m_readySet.clear(); m_drainingSet.clear(); m_verified.clear(); m_unverified.clear(); m_unknownSet.clear(); m_unknown.clear(); m_future.clear(); }
void clear();
/// Return first block with an unknown parent.
h256 firstUnknown() const { ReadGuard l(m_lock); return m_unknownSet.size() ? *m_unknownSet.begin() : h256(); }
@ -111,15 +111,28 @@ public:
QueueStatus blockStatus(h256 const& _h) const;
template <class T> Handler onReady(T const& _t) { return m_onReady.add(_t); }
template <class T> Handler onRoomAvailable(T const& _t) { return m_onRoomAvailable.add(_t); }
template <class T> void setOnBad(T const& _t) { m_onBad = _t; }
bool knownFull() const;
bool unknownFull() const;
private:
struct UnverifiedBlock
{
h256 hash;
h256 parentHash;
bytes block;
};
void noteReady_WITH_LOCK(h256 const& _b);
bool invariants() const override;
void verifierBody();
void collectUnknownBad(h256 const& _bad);
void updateBad(h256 const& _bad);
mutable boost::shared_mutex m_lock; ///< General lock for the sets, m_future and m_unknown.
h256Hash m_drainingSet; ///< All blocks being imported.
@ -129,17 +142,22 @@ private:
h256Hash m_knownBad; ///< Set of blocks that we know will never be valid.
std::multimap<unsigned, std::pair<h256, bytes>> m_future; ///< Set of blocks that are not yet valid. Ordered by timestamp
Signal m_onReady; ///< Called when a subsequent call to import blocks will return a non-empty container. Be nice and exit fast.
Signal m_onRoomAvailable; ///< Called when space for new blocks becomes availabe after a drain. Be nice and exit fast.
mutable Mutex m_verification; ///< Mutex that allows writing to m_verified, m_verifying and m_unverified.
std::condition_variable m_moreToVerify; ///< Signaled when m_unverified has a new entry.
std::vector<VerifiedBlock> m_verified; ///< List of blocks, in correct order, verified and ready for chain-import.
std::deque<VerifiedBlock> m_verifying; ///< List of blocks being verified; as long as the second component (bytes) is empty, it's not finished.
std::deque<std::pair<h256, bytes>> m_unverified; ///< List of blocks, in correct order, ready for verification.
std::vector<VerifiedBlock> m_verified; ///< List of blocks, in correct order, verified and ready for chain-import.
std::deque<VerifiedBlock> m_verifying; ///< List of blocks being verified; as long as the block component (bytes) is empty, it's not finished.
std::deque<UnverifiedBlock> m_unverified; ///< List of <block hash, parent hash, block data> in correct order, ready for verification.
std::vector<std::thread> m_verifiers; ///< Threads who only verify.
bool m_deleting = false; ///< Exit condition for verifiers.
std::function<void(Exception&)> m_onBad; ///< Called if we have a block that doesn't verify.
std::function<void(Exception&)> m_onBad; ///< Called if we have a block that doesn't verify.
std::atomic<size_t> m_unknownSize; ///< Tracks total size in bytes of all unknown blocks
std::atomic<size_t> m_knownSize; ///< Tracks total size in bytes of all known blocks;
std::atomic<size_t> m_unknownCount; ///< Tracks total count of unknown blocks. Used to avoid additional syncing
std::atomic<size_t> m_knownCount; ///< Tracks total count of known blocks. Used to avoid additional syncing
};
std::ostream& operator<<(std::ostream& _out, BlockQueueStatus const& _s);

101
libethereum/Client.cpp

@ -87,7 +87,7 @@ void VersionChecker::setOk()
}
}
void Client::onBadBlock(Exception& _ex)
void Client::onBadBlock(Exception& _ex) const
{
// BAD BLOCK!!!
bytes const* block = boost::get_error_info<errinfo_block>(_ex);
@ -127,6 +127,7 @@ void Client::onBadBlock(Exception& _ex)
if (string const* vmtraceJson = boost::get_error_info<errinfo_vmtrace>(_ex))
Json::Reader().parse(*vmtraceJson, report["hints"]["vmtrace"]);
if (vector<bytes> const* receipts = boost::get_error_info<errinfo_receipts>(_ex))
{
report["hints"]["receipts"] = Json::arrayValue;
@ -427,21 +428,17 @@ void Client::killChain()
void Client::clearPending()
{
h256Hash changeds;
DEV_WRITE_GUARDED(x_postMine)
{
if (!m_postMine.pending().size())
return;
// for (unsigned i = 0; i < m_postMine.pending().size(); ++i)
// appendFromNewPending(m_postMine.logBloom(i), changeds);
changeds.insert(PendingChangedFilter);
m_tq.clear();
DEV_READ_GUARDED(x_preMine)
m_postMine = m_preMine;
}
startMining();
h256Hash changeds;
noteChanged(changeds);
}
@ -464,47 +461,53 @@ static S& filtersStreamOut(S& _out, T const& _fs)
return _out;
}
void Client::appendFromNewPending(TransactionReceipt const& _receipt, h256Hash& io_changed, h256 _transactionHash)
void Client::appendFromNewPending(TransactionReceipt const& _receipt, h256Hash& io_changed, h256 _sha3)
{
Guard l(x_filtersWatches);
io_changed.insert(PendingChangedFilter);
m_specialFilters.at(PendingChangedFilter).push_back(_sha3);
for (pair<h256 const, InstalledFilter>& i: m_filters)
if (i.second.filter.envelops(RelativeBlock::Pending, m_bc.number() + 1))
{
// acceptable number.
auto m = i.second.filter.matches(_receipt);
if (m.size())
{
// acceptable number.
auto m = i.second.filter.matches(_receipt);
if (m.size())
{
// filter catches them
for (LogEntry const& l: m)
i.second.changes.push_back(LocalisedLogEntry(l, m_bc.number() + 1, _transactionHash));
io_changed.insert(i.first);
}
// filter catches them
for (LogEntry const& l: m)
i.second.changes.push_back(LocalisedLogEntry(l));
io_changed.insert(i.first);
}
}
}
void Client::appendFromNewBlock(h256 const& _block, h256Hash& io_changed)
{
// TODO: more precise check on whether the txs match.
auto d = m_bc.info(_block);
auto br = m_bc.receipts(_block);
auto receipts = m_bc.receipts(_block).receipts;
Guard l(x_filtersWatches);
io_changed.insert(ChainChangedFilter);
m_specialFilters.at(ChainChangedFilter).push_back(_block);
for (pair<h256 const, InstalledFilter>& i: m_filters)
if (i.second.filter.envelops(RelativeBlock::Latest, d.number) && i.second.filter.matches(d.logBloom))
// acceptable number & looks like block may contain a matching log entry.
for (size_t j = 0; j < br.receipts.size(); j++)
{
// acceptable number & looks like block may contain a matching log entry.
unsigned logIndex = 0;
for (size_t j = 0; j < receipts.size(); j++)
{
logIndex++;
auto tr = receipts[j];
auto m = i.second.filter.matches(tr);
if (m.size())
{
auto tr = br.receipts[j];
auto m = i.second.filter.matches(tr);
if (m.size())
{
auto transactionHash = transaction(d.hash(), j).sha3();
// filter catches them
for (LogEntry const& l: m)
i.second.changes.push_back(LocalisedLogEntry(l, (unsigned)d.number, transactionHash));
io_changed.insert(i.first);
}
auto transactionHash = transaction(d.hash(), j).sha3();
// filter catches them
for (LogEntry const& l: m)
i.second.changes.push_back(LocalisedLogEntry(l, d, transactionHash, j, logIndex));
io_changed.insert(i.first);
}
}
}
}
void Client::setForceMining(bool _enable)
@ -611,13 +614,10 @@ bool Client::submitWork(ProofOfWork::Solution const& _solution)
void Client::syncBlockQueue()
{
ImportRoute ir;
cwork << "BQ ==> CHAIN ==> STATE";
{
tie(ir.first, ir.second, m_syncBlockQueue) = m_bc.sync(m_bq, m_stateDB, rand() % 90 + 10);
if (ir.first.empty())
return;
}
tie(ir.first, ir.second, m_syncBlockQueue) = m_bc.sync(m_bq, m_stateDB, rand() % 10 + 5);
if (ir.first.empty())
return;
onChainChanged(ir);
}
@ -642,7 +642,7 @@ void Client::syncTransactionQueue()
DEV_READ_GUARDED(x_postMine)
for (size_t i = 0; i < newPendingReceipts.size(); i++)
appendFromNewPending(newPendingReceipts[i], changeds, m_postMine.pending()[i].sha3());
changeds.insert(PendingChangedFilter);
// Tell farm about new transaction (i.e. restartProofOfWork mining).
onPostStateChanged();
@ -685,7 +685,6 @@ void Client::onChainChanged(ImportRoute const& _ir)
h256Hash changeds;
for (auto const& h: _ir.first)
appendFromNewBlock(h, changeds);
changeds.insert(ChainChangedFilter);
// RESTART MINING
@ -791,15 +790,18 @@ void Client::noteChanged(h256Hash const& _filters)
cwatch << "!!!" << w.first << w.second.id.abridged();
w.second.changes += m_filters.at(w.second.id).changes;
}
else
{
cwatch << "!!!" << w.first << LogTag::Special << (w.second.id == PendingChangedFilter ? "pending" : w.second.id == ChainChangedFilter ? "chain" : "???");
w.second.changes.push_back(LocalisedLogEntry(SpecialLogEntry, 0));
}
else if (m_specialFilters.count(w.second.id))
for (h256 const& hash: m_specialFilters.at(w.second.id))
{
cwatch << "!!!" << w.first << LogTag::Special << (w.second.id == PendingChangedFilter ? "pending" : w.second.id == ChainChangedFilter ? "chain" : "???");
w.second.changes.push_back(LocalisedLogEntry(SpecialLogEntry, hash));
}
}
// clear the filters now.
for (auto& i: m_filters)
i.second.changes.clear();
for (auto& i: m_specialFilters)
i.second.clear();
}
void Client::doWork()
@ -859,7 +861,16 @@ void Client::checkWatchGarbage()
State Client::asOf(h256 const& _block) const
{
return State(m_stateDB, bc(), _block);
try
{
return State(m_stateDB, bc(), _block);
}
catch (Exception& ex)
{
ex << errinfo_block(bc().block(_block));
onBadBlock(ex);
return State();
}
}
void Client::prepareForTransaction()

2
libethereum/Client.h

@ -301,7 +301,7 @@ private:
/// Called when we have attempted to import a bad block.
/// @warning May be called from any thread.
void onBadBlock(Exception& _ex);
void onBadBlock(Exception& _ex) const;
VersionChecker m_vc; ///< Dummy object to check & update the protocol version.
CanonBlockChain m_bc; ///< Maintains block database.

33
libethereum/ClientBase.cpp

@ -171,8 +171,8 @@ LocalisedLogEntries ClientBase::logs(unsigned _watchId) const
LocalisedLogEntries ClientBase::logs(LogFilter const& _f) const
{
LocalisedLogEntries ret;
unsigned begin = min<unsigned>(bc().number() + 1, (unsigned)_f.latest());
unsigned end = min(bc().number(), min(begin, (unsigned)_f.earliest()));
unsigned begin = min(bc().number() + 1, (unsigned)numberFromHash(_f.latest()));
unsigned end = min(bc().number(), min(begin, (unsigned)numberFromHash(_f.earliest())));
// Handle pending transactions differently as they're not on the block chain.
if (begin > bc().number())
@ -182,11 +182,10 @@ LocalisedLogEntries ClientBase::logs(LogFilter const& _f) const
{
// Might have a transaction that contains a matching log.
TransactionReceipt const& tr = temp.receipt(i);
auto th = temp.pending()[i].sha3();
LogEntries le = _f.matches(tr);
if (le.size())
for (unsigned j = 0; j < le.size(); ++j)
ret.insert(ret.begin(), LocalisedLogEntry(le[j], begin, th));
ret.insert(ret.begin(), LocalisedLogEntry(le[j]));
}
begin = bc().number();
}
@ -201,20 +200,22 @@ LocalisedLogEntries ClientBase::logs(LogFilter const& _f) const
{
int total = 0;
auto h = bc().numberHash(n);
auto info = bc().info(h);
auto receipts = bc().receipts(h).receipts;
unsigned logIndex = 0;
for (size_t i = 0; i < receipts.size(); i++)
{
logIndex++;
TransactionReceipt receipt = receipts[i];
if (_f.matches(receipt.bloom()))
{
auto info = bc().info(h);
auto th = transaction(info.hash(), i).sha3();
LogEntries le = _f.matches(receipt);
if (le.size())
{
total += le.size();
for (unsigned j = 0; j < le.size(); ++j)
ret.insert(ret.begin(), LocalisedLogEntry(le[j], n, th));
ret.insert(ret.begin(), LocalisedLogEntry(le[j], info, th, i, logIndex));
}
}
@ -313,6 +314,8 @@ LocalisedLogEntries ClientBase::checkWatch(unsigned _watchId)
BlockInfo ClientBase::blockInfo(h256 _hash) const
{
if (_hash == PendingBlockHash)
return preMine().info();
return BlockInfo(bc().block(_hash));
}
@ -441,6 +444,24 @@ h256 ClientBase::hashFromNumber(BlockNumber _number) const
BlockNumber ClientBase::numberFromHash(h256 _blockHash) const
{
if (_blockHash == PendingBlockHash)
return bc().number() + 1;
else if (_blockHash == LatestBlockHash)
return bc().number();
else if (_blockHash == EarliestBlockHash)
return 0;
return bc().number(_blockHash);
}
int ClientBase::compareBlockHashes(h256 _h1, h256 _h2) const
{
BlockNumber n1 = numberFromHash(_h1);
BlockNumber n2 = numberFromHash(_h2);
if (n1 > n2) {
return 1;
} else if (n1 == n2) {
return 0;
}
return -1;
}

5
libethereum/ClientBase.h

@ -44,7 +44,7 @@ static const h256 PendingChangedFilter = u256(0);
static const h256 ChainChangedFilter = u256(1);
static const LogEntry SpecialLogEntry = LogEntry(Address(), h256s(), bytes());
static const LocalisedLogEntry InitialChange(SpecialLogEntry, 0);
static const LocalisedLogEntry InitialChange(SpecialLogEntry);
struct ClientWatch
{
@ -117,6 +117,7 @@ public:
virtual h256 hashFromNumber(BlockNumber _number) const override;
virtual BlockNumber numberFromHash(h256 _blockHash) const override;
virtual int compareBlockHashes(h256 _h1, h256 _h2) const override;
virtual BlockInfo blockInfo(h256 _hash) const override;
virtual BlockDetails blockDetails(h256 _hash) const override;
virtual Transaction transaction(h256 _transactionHash) const override;
@ -175,6 +176,8 @@ protected:
// filters
mutable Mutex x_filtersWatches; ///< Our lock.
std::unordered_map<h256, InstalledFilter> m_filters; ///< The dictionary of filters that are active.
std::unordered_map<h256, h256s> m_specialFilters = std::unordered_map<h256, std::vector<h256>>{{PendingChangedFilter, {}}, {ChainChangedFilter, {}}};
///< The dictionary of special filters and their additional data
std::map<unsigned, ClientWatch> m_watches; ///< Each and every watch - these reference a filter.
};

4
libethereum/CommonNet.h

@ -38,9 +38,9 @@ namespace eth
#if ETH_DEBUG
static const unsigned c_maxHashes = 2048; ///< Maximum number of hashes BlockHashes will ever send.
static const unsigned c_maxHashesAsk = 256; ///< Maximum number of hashes GetBlockHashes will ever ask for.
static const unsigned c_maxHashesAsk = 2048; ///< Maximum number of hashes GetBlockHashes will ever ask for.
static const unsigned c_maxBlocks = 128; ///< Maximum number of blocks Blocks will ever send.
static const unsigned c_maxBlocksAsk = 8; ///< Maximum number of blocks we ask to receive in Blocks (when using GetChain).
static const unsigned c_maxBlocksAsk = 128; ///< Maximum number of blocks we ask to receive in Blocks (when using GetChain).
static const unsigned c_maxPayload = 262144; ///< Maximum size of packet for us to send.
#else
static const unsigned c_maxHashes = 2048; ///< Maximum number of hashes BlockHashes will ever send.

112
libethereum/EthereumHost.cpp

@ -39,6 +39,7 @@ using namespace dev::eth;
using namespace p2p;
unsigned const EthereumHost::c_oldProtocolVersion = 60; //TODO: remove this once v61+ is common
unsigned const c_chainReorgSize = 30000;
EthereumHost::EthereumHost(BlockChain const& _ch, TransactionQueue& _tq, BlockQueue& _bq, u256 _networkId):
HostCapability<EthereumPeer>(),
@ -50,6 +51,7 @@ EthereumHost::EthereumHost(BlockChain const& _ch, TransactionQueue& _tq, BlockQu
{
m_latestBlockSent = _ch.currentHash();
m_hashMan.reset(m_chain.number() + 1);
m_bqRoomAvailable = m_bq.onRoomAvailable([this](){ m_continueSync = true; });
}
EthereumHost::~EthereumHost()
@ -91,7 +93,7 @@ void EthereumHost::doWork()
bool netChange = ensureInitialised();
auto h = m_chain.currentHash();
// If we've finished our initial sync (including getting all the blocks into the chain so as to reduce invalid transactions), start trading transactions & blocks
if (isSyncing() && m_chain.isKnown(m_latestBlockSent))
if (!isSyncing() && m_chain.isKnown(m_latestBlockSent))
{
if (m_newTransactions)
{
@ -105,6 +107,12 @@ void EthereumHost::doWork()
}
}
if (m_continueSync)
{
m_continueSync = false;
continueSync();
}
foreachPeer([](EthereumPeer* _p) { _p->tick(); });
// return netChange;
@ -144,6 +152,7 @@ void EthereumHost::maintainTransactions()
RLPStream ts;
_p->prep(ts, TransactionsPacket, n).appendRaw(b, n);
_p->sealAndSend(ts);
cnote << "Sent" << n << "transactions to " << _p->session()->info().clientVersion;
}
_p->m_requireTransactions = false;
});
@ -250,37 +259,43 @@ void EthereumHost::onPeerStatus(EthereumPeer* _peer)
_peer->disable("Peer banned for previous bad behaviour.");
else
{
if (_peer->m_protocolVersion != protocolVersion())
estimatePeerHashes(_peer);
else
unsigned estimatedHashes = estimateHashes();
if (_peer->m_protocolVersion == protocolVersion())
{
if (_peer->m_latestBlockNumber > m_chain.number())
_peer->m_expectedHashes = (unsigned)_peer->m_latestBlockNumber - m_chain.number();
if (m_hashMan.chainSize() < _peer->m_expectedHashes)
if (_peer->m_expectedHashes > estimatedHashes)
_peer->disable("Too many hashes");
else if (m_needSyncHashes && m_hashMan.chainSize() < _peer->m_expectedHashes)
m_hashMan.resetToRange(m_chain.number() + 1, _peer->m_expectedHashes);
}
else
_peer->m_expectedHashes = estimatedHashes;
continueSync(_peer);
}
}
void EthereumHost::estimatePeerHashes(EthereumPeer* _peer)
unsigned EthereumHost::estimateHashes()
{
BlockInfo block = m_chain.info();
time_t lastBlockTime = (block.hash() == m_chain.genesisHash()) ? 1428192000 : (time_t)block.timestamp;
time_t now = time(0);
unsigned blockCount = 30000;
unsigned blockCount = c_chainReorgSize;
if (lastBlockTime > now)
clog(NetWarn) << "Clock skew? Latest block is in the future";
else
blockCount += (now - lastBlockTime) / (unsigned)c_durationLimit;
clog(NetAllDetail) << "Estimated hashes: " << blockCount;
_peer->m_expectedHashes = blockCount;
return blockCount;
}
void EthereumHost::onPeerHashes(EthereumPeer* _peer, h256s const& _hashes)
{
RecursiveGuard l(x_sync);
assert(_peer->m_asking == Asking::Nothing);
if (_peer->m_syncHashNumber > 0)
_peer->m_syncHashNumber += _hashes.size();
_peer->setAsking(Asking::Nothing);
onPeerHashes(_peer, _hashes, false);
}
@ -294,7 +309,7 @@ void EthereumHost::onPeerHashes(EthereumPeer* _peer, h256s const& _hashes, bool
}
bool syncByNumber = _peer->m_syncHashNumber;
if (!syncByNumber && _peer->m_syncHash != m_syncingLatestHash)
if (!syncByNumber && !_complete && _peer->m_syncHash != m_syncingLatestHash)
{
// Obsolete hashes, discard
continueSync(_peer);
@ -353,7 +368,7 @@ void EthereumHost::onPeerHashes(EthereumPeer* _peer, h256s const& _hashes, bool
if (_complete)
{
m_needSyncBlocks = true;
continueSync(_peer);
continueSync();
}
else if (syncByNumber && m_hashMan.isComplete())
{
@ -392,7 +407,7 @@ void EthereumHost::onPeerDoneHashes(EthereumPeer* _peer, bool _localChain)
void EthereumHost::onPeerBlocks(EthereumPeer* _peer, RLP const& _r)
{
RecursiveGuard l(x_sync);
assert(_peer->m_asking == Asking::Nothing);
_peer->setAsking(Asking::Nothing);
unsigned itemCount = _r.itemCount();
clog(NetMessageSummary) << "Blocks (" << dec << itemCount << "entries)" << (itemCount ? "" : ": NoMoreBlocks");
@ -453,35 +468,26 @@ void EthereumHost::onPeerBlocks(EthereumPeer* _peer, RLP const& _r)
}
clog(NetMessageSummary) << dec << success << "imported OK," << unknown << "with unknown parents," << future << "with future timestamps," << got << " already known," << repeated << " repeats received.";
if (m_man.isComplete() && !m_needSyncHashes)
{
// Done our chain-get.
m_needSyncBlocks = false;
clog(NetNote) << "Chain download complete.";
// 1/100th for each useful block hash.
_peer->addRating(m_man.chainSize() / 100); //TODO: what about other peers?
m_man.reset();
}
continueSync(_peer);
}
void EthereumHost::onPeerNewHashes(EthereumPeer* _peer, h256s const& _hashes)
{
RecursiveGuard l(x_sync);
if (isSyncing_UNSAFE())
if (isSyncing_UNSAFE() || _peer->isConversing())
{
clog(NetMessageSummary) << "Ignoring new hashes since we're already downloading.";
return;
}
clog(NetNote) << "New block hash discovered: syncing without help.";
_peer->m_syncHashNumber = 0;
onPeerHashes(_peer, _hashes, true);
}
void EthereumHost::onPeerNewBlock(EthereumPeer* _peer, RLP const& _r)
{
RecursiveGuard l(x_sync);
if (isSyncing_UNSAFE())
if (isSyncing_UNSAFE() || _peer->isConversing())
{
clog(NetMessageSummary) << "Ignoring new blocks since we're already downloading.";
return;
@ -535,7 +541,7 @@ void EthereumHost::onPeerNewBlock(EthereumPeer* _peer, RLP const& _r)
_peer->m_knownBlocks.insert(h);
if (sync)
continueSync(_peer);
continueSync();
}
}
@ -581,7 +587,7 @@ void EthereumHost::onPeerAborting(EthereumPeer* _peer)
void EthereumHost::continueSync()
{
clog(NetAllDetail) << "Getting help with downloading hashes and blocks";
clog(NetAllDetail) << "Continuing sync for all peers";
foreachPeer([&](EthereumPeer* _p)
{
if (_p->m_asking == Asking::Nothing)
@ -594,8 +600,14 @@ void EthereumHost::continueSync(EthereumPeer* _peer)
assert(_peer->m_asking == Asking::Nothing);
bool otherPeerV60Sync = false;
bool otherPeerV61Sync = false;
if (m_needSyncHashes && peerShouldGrabChain(_peer))
if (m_needSyncHashes)
{
if (!peerShouldGrabChain(_peer))
{
_peer->setIdle();
return;
}
foreachPeer([&](EthereumPeer* _p)
{
if (_p != _peer && _p->m_asking == Asking::Hashes)
@ -641,8 +653,38 @@ void EthereumHost::continueSync(EthereumPeer* _peer)
_peer->setIdle();
}
}
else if (m_needSyncBlocks && peerCanHelp(_peer)) // Check if this peer can help with downloading blocks
_peer->requestBlocks();
else if (m_needSyncBlocks)
{
if (m_man.isComplete())
{
// Done our chain-get.
m_needSyncBlocks = false;
clog(NetNote) << "Chain download complete.";
// 1/100th for each useful block hash.
_peer->addRating(m_man.chainSize() / 100); //TODO: what about other peers?
m_man.reset();
_peer->setIdle();
return;
}
else if (peerCanHelp(_peer))
{
// Check block queue status
if (m_bq.unknownFull())
{
clog(NetWarn) << "Too many unknown blocks, restarting sync";
m_bq.clear();
reset();
continueSync();
}
else if (m_bq.knownFull())
{
clog(NetAllDetail) << "Waiting for block queue before downloading blocks";
_peer->setIdle();
}
else
_peer->requestBlocks();
}
}
else
_peer->setIdle();
}
@ -693,15 +735,7 @@ bool EthereumHost::peerShouldGrabChain(EthereumPeer* _peer) const
bool EthereumHost::isSyncing_UNSAFE() const
{
/// We need actual peer information here to handle the case when we are the first ever peer on the network to mine.
/// I.e. on a new private network the first node mining has noone to sync with and should start block propogation immediately.
bool syncing = false;
foreachPeer([&](EthereumPeer* _p)
{
if (_p->m_asking != Asking::Nothing)
syncing = true;
});
return syncing;
return m_needSyncBlocks || m_needSyncHashes;
}
HashChainStatus EthereumHost::status()
@ -709,6 +743,6 @@ HashChainStatus EthereumHost::status()
RecursiveGuard l(x_sync);
if (m_syncingV61)
return HashChainStatus { static_cast<unsigned>(m_hashMan.chainSize()), static_cast<unsigned>(m_hashMan.gotCount()), false };
return HashChainStatus { m_estimatedHashes - 30000, static_cast<unsigned>(m_hashes.size()), true };
return HashChainStatus { m_estimatedHashes > 0 ? m_estimatedHashes - c_chainReorgSize : 0, static_cast<unsigned>(m_hashes.size()), m_estimatedHashes > 0 };
}

3
libethereum/EthereumHost.h

@ -125,11 +125,13 @@ private:
bool peerShouldGrabBlocks(EthereumPeer* _peer) const;
bool peerShouldGrabChain(EthereumPeer* _peer) const;
bool peerCanHelp(EthereumPeer* _peer) const;
unsigned estimateHashes();
void estimatePeerHashes(EthereumPeer* _peer);
BlockChain const& m_chain;
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).
Handler m_bqRoomAvailable;
u256 m_networkId;
@ -152,6 +154,7 @@ private:
h256s m_hashes; ///< List of hashes with unknown block numbers. Used for PV60 chain downloading and catching up to a particular unknown
unsigned m_estimatedHashes = 0; ///< Number of estimated hashes for the last peer over PV60. Used for status reporting only.
bool m_syncingV61 = false; ///< True if recent activity was over pv61+. Used for status reporting only.
bool m_continueSync = false; ///< True when the block queue has processed a block; we should restart grabbing blocks.
};
}

6
libethereum/EthereumPeer.cpp

@ -265,13 +265,10 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
clog(NetWarn) << "Peer giving us hashes when we didn't ask for them.";
break;
}
setAsking(Asking::Nothing);
h256s hashes(itemCount);
for (unsigned i = 0; i < itemCount; ++i)
hashes[i] = _r[i].toHash<h256>();
if (m_syncHashNumber > 0)
m_syncHashNumber += itemCount;
host()->onPeerHashes(this, hashes);
break;
}
@ -314,10 +311,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
if (m_asking != Asking::Blocks)
clog(NetImpolite) << "Peer giving us blocks when we didn't ask for them.";
else
{
setAsking(Asking::Nothing);
host()->onPeerBlocks(this, _r);
}
break;
}
case NewBlockPacket:

36
libethereum/Executive.cpp

@ -46,6 +46,8 @@ bool changesMemory(Instruction _inst)
return
_inst == Instruction::MSTORE ||
_inst == Instruction::MSTORE8 ||
_inst == Instruction::MLOAD ||
_inst == Instruction::CREATE ||
_inst == Instruction::CALL ||
_inst == Instruction::CALLCODE ||
_inst == Instruction::SHA3 ||
@ -71,6 +73,7 @@ void StandardTrace::operator()(uint64_t _steps, Instruction inst, bigint newMemS
stack.append(toHex(toCompactBigEndian(i), 1));
r["stack"] = stack;
bool returned = false;
bool newContext = false;
Instruction lastInst = Instruction::STOP;
@ -84,6 +87,7 @@ void StandardTrace::operator()(uint64_t _steps, Instruction inst, bigint newMemS
else if (m_lastInst.size() == ext.depth + 2)
{
// returned from old context
returned = true;
m_lastInst.pop_back();
lastInst = m_lastInst.back();
}
@ -91,6 +95,7 @@ void StandardTrace::operator()(uint64_t _steps, Instruction inst, bigint newMemS
{
// continuing in previous context
lastInst = m_lastInst.back();
m_lastInst.back() = inst;
}
else
{
@ -101,10 +106,10 @@ void StandardTrace::operator()(uint64_t _steps, Instruction inst, bigint newMemS
if (changesMemory(lastInst) || newContext)
{
Json::Value mem(Json::arrayValue);
for (auto const& i: vm.memory())
mem.append(toHex(toCompactBigEndian(i), 1));
r["memory"] = mem;
if (vm.memory().size() < 1024)
r["memory"] = toHex(vm.memory());
else
r["sha3memory"] = sha3(vm.memory()).hex();
}
if (changesStorage(lastInst) || newContext)
@ -115,21 +120,26 @@ void StandardTrace::operator()(uint64_t _steps, Instruction inst, bigint newMemS
r["storage"] = storage;
}
r["depth"] = ext.depth;
r["address"] = ext.myAddress.hex();
if (returned || newContext)
r["depth"] = ext.depth;
if (newContext)
r["address"] = ext.myAddress.hex();
r["steps"] = (unsigned)_steps;
r["inst"] = (unsigned)inst;
if (m_showMnemonics)
r["instname"] = instructionInfo(inst).name;
r["pc"] = toString(vm.curPC());
r["gas"] = toString(gas);
r["gascost"] = toString(gasCost);
r["memexpand"] = toString(newMemSize);
if (!!newMemSize)
r["memexpand"] = toString(newMemSize);
m_trace->append(r);
}
string StandardTrace::json() const
string StandardTrace::json(bool _styled) const
{
return Json::FastWriter().write(*m_trace);
return _styled ? Json::StyledWriter().write(*m_trace) : Json::FastWriter().write(*m_trace);
}
Executive::Executive(State& _s, BlockChain const& _bc, unsigned _level):
@ -191,12 +201,12 @@ void Executive::initialize(Transaction const& _transaction)
// Avoid unaffordable transactions.
m_gasCost = (bigint)m_t.gas() * m_t.gasPrice();
m_totalCost = m_t.value() + m_gasCost;
if (m_s.balance(m_t.sender()) < m_totalCost)
bigint totalCost = m_t.value() + m_gasCost;
if (m_s.balance(m_t.sender()) < totalCost)
{
clog(ExecutiveWarnChannel) << "Not enough cash: Require >" << m_totalCost << " Got" << m_s.balance(m_t.sender()) << "for sender: " << m_t.sender();
clog(ExecutiveWarnChannel) << "Not enough cash: Require >" << totalCost << " Got" << m_s.balance(m_t.sender()) << "for sender: " << m_t.sender();
m_excepted = TransactionException::NotEnoughCash;
BOOST_THROW_EXCEPTION(NotEnoughCash() << RequirementError(m_totalCost, (bigint)m_s.balance(m_t.sender())) << errinfo_comment(m_t.sender().abridged()));
BOOST_THROW_EXCEPTION(NotEnoughCash() << RequirementError(totalCost, (bigint)m_s.balance(m_t.sender())) << errinfo_comment(m_t.sender().abridged()));
}
}

11
libethereum/Executive.h

@ -49,9 +49,12 @@ public:
StandardTrace();
void operator()(uint64_t _steps, Instruction _inst, bigint _newMemSize, bigint _gasCost, bigint _gas, VM* _vm, ExtVMFace const* _extVM);
std::string json() const;
void setShowMnemonics() { m_showMnemonics = true; }
std::string json(bool _styled = false) const;
private:
bool m_showMnemonics = false;
std::vector<Instruction> m_lastInst;
std::shared_ptr<Json::Value> m_trace;
};
@ -83,8 +86,6 @@ public:
Executive(State& _s, LastHashes const& _lh, unsigned _level = 0): m_s(_s), m_lastHashes(_lh), m_depth(_level) {}
/// Basic constructor.
Executive(State& _s, BlockChain const& _bc, unsigned _level = 0);
/// Basic destructor.
~Executive() = default;
Executive(Executive const&) = delete;
void operator=(Executive) = delete;
@ -142,7 +143,7 @@ public:
private:
State& m_s; ///< The state to which this operation/transaction is applied.
LastHashes m_lastHashes;
std::shared_ptr<ExtVM> m_ext; ///< The VM externality object for the VM execution or null if no VM is required.
std::shared_ptr<ExtVM> m_ext; ///< The VM externality object for the VM execution or null if no VM is required. shared_ptr used only to allow ExtVM forward reference.
bytesRef m_outRef; ///< Reference to "expected output" buffer.
ExecutionResult* m_res = nullptr; ///< Optional storage for execution results.
Address m_newAddress; ///< The address of the created contract in the case of create() being called.
@ -155,9 +156,7 @@ private:
Transaction m_t; ///< The original transaction. Set by setup().
LogEntries m_logs; ///< The log entries created by this transaction. Set by finalize().
bigint m_gasRequired; ///< Gas required during execution of the transaction.
bigint m_gasCost;
bigint m_totalCost;
};
}

1
libethereum/Interface.h

@ -137,6 +137,7 @@ public:
virtual std::pair<h256, unsigned> transactionLocation(h256 const& _transactionHash) const = 0;
virtual h256 hashFromNumber(BlockNumber _number) const = 0;
virtual BlockNumber numberFromHash(h256 _blockHash) const = 0;
virtual int compareBlockHashes(h256 _h1, h256 _h2) const = 0;
virtual BlockInfo blockInfo(h256 _hash) const = 0;
virtual BlockDetails blockDetails(h256 _hash) const = 0;

27
libethereum/LogFilter.cpp

@ -46,33 +46,6 @@ h256 LogFilter::sha3() const
return dev::sha3(s.out());
}
static bool isNoLater(RelativeBlock _logBlockRelation, u256 _logBlockNumber, unsigned _latest)
{
if (_latest == PendingBlock)
return true;
else if (_latest == LatestBlock)
return _logBlockRelation == RelativeBlock::Latest;
else
return _logBlockNumber <= _latest;
}
static bool isNoEarlier(RelativeBlock _logBlockRelation, u256 _logBlockNumber, unsigned _earliest)
{
if (_earliest == PendingBlock)
return _logBlockRelation == RelativeBlock::Pending;
else if (_earliest == LatestBlock)
return true;
else
return _logBlockNumber >= _earliest;
}
bool LogFilter::envelops(RelativeBlock _logBlockRelation, u256 _logBlockNumber) const
{
return
isNoLater(_logBlockRelation, _logBlockNumber, m_latest) &&
isNoEarlier(_logBlockRelation, _logBlockNumber, m_earliest);
}
bool LogFilter::matches(LogBloom _bloom) const
{
if (m_addresses.size())

15
libethereum/LogFilter.h

@ -45,15 +45,14 @@ class State;
class LogFilter
{
public:
LogFilter(unsigned _earliest = 0, unsigned _latest = PendingBlock): m_earliest(_earliest), m_latest(_latest) {}
LogFilter(h256 _earliest = EarliestBlockHash, h256 _latest = PendingBlockHash): m_earliest(_earliest), m_latest(_latest) {}
void streamRLP(RLPStream& _s) const;
h256 sha3() const;
unsigned earliest() const { return m_earliest; }
unsigned latest() const { return m_latest; }
h256 earliest() const { return m_earliest; }
h256 latest() const { return m_latest; }
bool envelops(RelativeBlock _logBlockRelation, u256 _logBlockNumber) const;
std::vector<LogBloom> bloomPossibilities() const;
bool matches(LogBloom _bloom) const;
bool matches(State const& _s, unsigned _i) const;
@ -61,16 +60,16 @@ public:
LogFilter address(Address _a) { m_addresses.insert(_a); return *this; }
LogFilter topic(unsigned _index, h256 const& _t) { if (_index < 4) m_topics[_index].insert(_t); return *this; }
LogFilter withEarliest(int _e) { m_earliest = _e; return *this; }
LogFilter withLatest(int _e) { m_latest = _e; return *this; }
LogFilter withEarliest(h256 _e) { m_earliest = _e; return *this; }
LogFilter withLatest(h256 _e) { m_latest = _e; return *this; }
friend std::ostream& dev::eth::operator<<(std::ostream& _out, dev::eth::LogFilter const& _s);
private:
AddressHash m_addresses;
std::array<h256Hash, 4> m_topics;
unsigned m_earliest = 0;
unsigned m_latest = LatestBlock;
h256 m_earliest = EarliestBlockHash;
h256 m_latest = PendingBlockHash;
};
}

22
libethereum/State.cpp

@ -594,6 +594,7 @@ string State::vmTrace(bytesConstRef _block, BlockChain const& _bc, ImportRequire
for (auto const& tr: rlp[1])
{
StandardTrace st;
st.setShowMnemonics();
execute(lh, Transaction(tr.data(), CheckTransaction::Everything), Permanence::Committed, [&](uint64_t _steps, Instruction _inst, bigint _newMemSize, bigint _gasCost, bigint _gas, VM* _vm, ExtVMFace const* _extVM) { st(_steps, _inst, _newMemSize, _gasCost, _gas, _vm, _extVM); });
ret += (ret.empty() ? "[" : ",") + st.json();
@ -777,7 +778,8 @@ void State::cleanup(bool _fullCommit)
paranoia("immediately before database commit", true);
// Commit the new trie to disk.
clog(StateTrace) << "Committing to disk: stateRoot" << m_currentBlock.stateRoot << "=" << rootHash() << "=" << toHex(asBytes(m_db.lookup(rootHash())));
if (isChannelVisible<StateTrace>()) // Avoid calling toHex if not needed
clog(StateTrace) << "Committing to disk: stateRoot" << m_currentBlock.stateRoot << "=" << rootHash() << "=" << toHex(asBytes(m_db.lookup(rootHash())));
try {
EnforceRefs er(m_db, true);
@ -790,7 +792,8 @@ void State::cleanup(bool _fullCommit)
}
m_db.commit();
clog(StateTrace) << "Committed: stateRoot" << m_currentBlock.stateRoot << "=" << rootHash() << "=" << toHex(asBytes(m_db.lookup(rootHash())));
if (isChannelVisible<StateTrace>()) // Avoid calling toHex if not needed
clog(StateTrace) << "Committed: stateRoot" << m_currentBlock.stateRoot << "=" << rootHash() << "=" << toHex(asBytes(m_db.lookup(rootHash())));
paranoia("immediately after database commit", true);
m_previousBlock = m_currentBlock;
@ -1214,21 +1217,6 @@ ExecutionResult State::execute(LastHashes const& _lh, Transaction const& _t, Per
else
e.go(_onOp);
}
#elif ETH_VMTIMER
{
(void)_onOp;
boost::timer t;
unordered_map<byte, unsigned> counts;
unsigned total = 0;
e.go([&](uint64_t, Instruction inst, bigint, bigint, bigint, VM*, ExtVMFace const*) {
counts[(byte)inst]++;
total++;
});
cnote << total << "total in" << t.elapsed();
for (auto const& c: {Instruction::SSTORE, Instruction::SLOAD, Instruction::CALL, Instruction::CREATE, Instruction::CALLCODE, Instruction::MSTORE8, Instruction::MSTORE, Instruction::MLOAD, Instruction::SHA3})
cnote << instructionInfo(c).name << counts[(byte)c];
cnote;
}
#else
e.go(_onOp);
#endif

26
libevm/ExtVMFace.h

@ -63,10 +63,28 @@ using LogEntries = std::vector<LogEntry>;
struct LocalisedLogEntry: public LogEntry
{
LocalisedLogEntry() {}
LocalisedLogEntry(LogEntry const& _le, unsigned _number, h256 _transactionHash = h256()): LogEntry(_le), number(_number), transactionHash(_transactionHash) {}
unsigned number = 0;
h256 transactionHash;
explicit LocalisedLogEntry(LogEntry const& _le): LogEntry(_le) {};
explicit LocalisedLogEntry(
LogEntry const& _le,
h256 _special
): LogEntry(_le), special(_special) {};
explicit LocalisedLogEntry(
LogEntry const& _le,
BlockInfo const& _bi,
h256 _th,
unsigned _ti,
unsigned _li
): LogEntry(_le), blockHash(_bi.hash()), blockNumber((BlockNumber)_bi.number), transactionHash(_th), transactionIndex(_ti), logIndex(_li), mined(true) {};
h256 blockHash = h256();
BlockNumber blockNumber = 0;
h256 transactionHash = h256();
unsigned transactionIndex = 0;
unsigned logIndex = 0;
bool mined = false;
h256 special = h256();
};
using LocalisedLogEntries = std::vector<LocalisedLogEntry>;

14
libevm/VMFace.h

@ -25,14 +25,14 @@ namespace dev
namespace eth
{
#define ETH_SIMPLE_EXCEPTION_VM(X) struct X: virtual VMException { public X(): VMException(#X) {} };
struct VMException: virtual Exception {};
struct BreakPointHit: virtual VMException {};
struct BadInstruction: virtual VMException {};
struct BadJumpDestination: virtual VMException {};
struct OutOfGas: virtual VMException {};
struct OutOfStack: virtual VMException {};
struct StackUnderflow: virtual VMException {};
#define ETH_SIMPLE_EXCEPTION_VM(X) struct X: virtual VMException { const char* what() const noexcept override { return #X; } }
ETH_SIMPLE_EXCEPTION_VM(BreakPointHit);
ETH_SIMPLE_EXCEPTION_VM(BadInstruction);
ETH_SIMPLE_EXCEPTION_VM(BadJumpDestination);
ETH_SIMPLE_EXCEPTION_VM(OutOfGas);
ETH_SIMPLE_EXCEPTION_VM(OutOfStack);
ETH_SIMPLE_EXCEPTION_VM(StackUnderflow);
/// EVM Virtual Machine interface
class VMFace

21
libevmasm/CommonSubexpressionEliminator.cpp

@ -35,6 +35,19 @@ vector<AssemblyItem> CommonSubexpressionEliminator::getOptimizedItems()
{
optimizeBreakingItem();
KnownState nextInitialState = m_state;
if (m_breakingItem)
nextInitialState.feedItem(*m_breakingItem);
KnownState nextState = nextInitialState;
ScopeGuard reset([&]()
{
m_breakingItem = nullptr;
m_storeOperations.clear();
m_initialState = move(nextInitialState);
m_state = move(nextState);
});
map<int, Id> initialStackContents;
map<int, Id> targetStackContents;
int minHeight = m_state.stackHeight() + 1;
@ -52,15 +65,7 @@ vector<AssemblyItem> CommonSubexpressionEliminator::getOptimizedItems()
targetStackContents
);
if (m_breakingItem)
{
items.push_back(*m_breakingItem);
m_state.feedItem(*m_breakingItem);
}
// cleanup
m_initialState = m_state;
m_breakingItem = nullptr;
m_storeOperations.clear();
return items;
}

2
libjsqrc/ethereumjs/.jshintrc

@ -9,7 +9,7 @@
"maxdepth": 3,
"maxerr": 50,
/*"maxlen": 80*/ /*this should be our goal*/
"maxparams": 3,
/*"maxparams": 3,*/
"nonew": true,
"unused": true,
"undef": true,

7
libjsqrc/ethereumjs/.versions

@ -1,4 +1,3 @@
3stack:bignumber@2.0.0
ethereum:js@0.0.15-rc12
meteor@1.1.4
underscore@1.0.2
ethereum:web3@0.5.0
meteor@1.1.6
underscore@1.0.3

5
libjsqrc/ethereumjs/bower.json

@ -1,14 +1,15 @@
{
"name": "web3",
"namespace": "ethereum",
"version": "0.4.2",
"version": "0.5.0",
"description": "Ethereum Compatible JavaScript API",
"main": [
"./dist/web3.js",
"./dist/web3.min.js"
],
"dependencies": {
"bignumber.js": ">=2.0.0"
"bignumber.js": ">=2.0.0",
"crypto-js": "~3.1.4"
},
"repository": {
"type": "git",

1877
libjsqrc/ethereumjs/dist/web3-light.js

File diff suppressed because one or more lines are too long

71
libjsqrc/ethereumjs/dist/web3-light.js.map

File diff suppressed because one or more lines are too long

4
libjsqrc/ethereumjs/dist/web3-light.min.js

File diff suppressed because one or more lines are too long

1879
libjsqrc/ethereumjs/dist/web3.js

File diff suppressed because one or more lines are too long

71
libjsqrc/ethereumjs/dist/web3.js.map

File diff suppressed because one or more lines are too long

4
libjsqrc/ethereumjs/dist/web3.min.js

File diff suppressed because one or more lines are too long

23
libjsqrc/ethereumjs/example/contract.html

@ -16,23 +16,10 @@
" }\n" +
"}\n";
var code = web3.eth.compile.solidity(source).code;
/*var code = "605280600c6000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063c6888fa114602e57005b60376004356041565b8060005260206000f35b6000600782029050604d565b91905056";*/
// contract description, this is autogenerated using solc CLI
var desc = [{
"constant" : true,
"inputs" : [{
"name" : "a",
"type" : "uint256"
}],
"name" : "multiply",
"outputs" : [{
"name" : "d",
"type" : "uint256"
}],
"type" : "function"
}];
var compiled = web3.eth.compile.solidity(source);
var code = compiled.test.code;
// contract json abi, this is autogenerated using solc CLI
var abi = compiled.test.info.abiDefinition;
var myContract;
@ -47,7 +34,7 @@
var watch = web3.eth.filter('latest');
// create contract
myContract = web3.eth.contract(desc).new({data: code});
myContract = web3.eth.contract(abi).new({data: code});
console.log('address: ' + myContract.address);
document.getElementById('status').innerText = "transaction sent, waiting for confirmation";
watch.watch(function (err, hash) {

29
libjsqrc/ethereumjs/example/event_inc.html

@ -18,29 +18,10 @@
" } " +
" uint x; " +
"}";
var code = web3.eth.compile.solidity(source).code;
/*var code = "5b60456000600050819055505b608c8060196000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063371303c014602e57005b6034603a565b60006000f35b6000600081815054600101919050819055506001600260006000505406147f6e61ef44ac2747ff8b84d353a908eb8bd5c3fb118334d57698c5cfc7041196ad600060006000505481526020016000a25b56";*/
var desc = [{
"constant" : false,
"inputs" : [],
"name" : "inc",
"outputs" : [],
"type" : "function"
}, {
"anonymous" : false,
"inputs" : [{
"indexed" : true,
"name" : "odd",
"type" : "bool"
}, {
"indexed" : false,
"name" : "x",
"type" : "uint256"
}],
"name" : "Incremented",
"type" : "event"
}];
var compiled = web3.eth.compile.solidity(source);
var code = compiled.Contract.code;
var abi = compiled.Contract.info.abiDefinition;
var address;
var contract;
@ -55,7 +36,7 @@
var watch = web3.eth.filter('latest');
contract = web3.eth.contract(desc).new({data: code});
contract = web3.eth.contract(abi).new({data: code});
console.log('address: ' + contract.address);

203
libjsqrc/ethereumjs/example/icap.html

@ -0,0 +1,203 @@
<!doctype>
<html>
<head>
<script type="text/javascript" src="../dist/web3.js"></script>
<script type="text/javascript">
var web3 = require('web3');
var BigNumber = require('bignumber.js');
web3.setProvider(new web3.providers.HttpProvider("http://localhost:8545"));
var from = web3.eth.coinbase;
web3.eth.defaultAccount = from;
var nameregAbi = [
{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"name","outputs":[{"name":"o_name","type":"bytes32"}],"type":"function"},
{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"owner","outputs":[{"name":"","type":"address"}],"type":"function"},
{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"content","outputs":[{"name":"","type":"bytes32"}],"type":"function"},
{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"addr","outputs":[{"name":"","type":"address"}],"type":"function"},
{"constant":false,"inputs":[{"name":"_name","type":"bytes32"}],"name":"reserve","outputs":[],"type":"function"},
{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"subRegistrar","outputs":[{"name":"o_subRegistrar","type":"address"}],"type":"function"},
{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_newOwner","type":"address"}],"name":"transfer","outputs":[],"type":"function"},
{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_registrar","type":"address"}],"name":"setSubRegistrar","outputs":[],"type":"function"},
{"constant":false,"inputs":[],"name":"Registrar","outputs":[],"type":"function"},
{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_a","type":"address"},{"name":"_primary","type":"bool"}],"name":"setAddress","outputs":[],"type":"function"},
{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_content","type":"bytes32"}],"name":"setContent","outputs":[],"type":"function"},
{"constant":false,"inputs":[{"name":"_name","type":"bytes32"}],"name":"disown","outputs":[],"type":"function"},
{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"register","outputs":[{"name":"","type":"address"}],"type":"function"},
{"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"bytes32"}],"name":"Changed","type":"event"},
{"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"bytes32"},{"indexed":true,"name":"addr","type":"address"}],"name":"PrimaryChanged","type":"event"}
];
var depositAbi = [{"constant":false,"inputs":[{"name":"name","type":"bytes32"}],"name":"deposit","outputs":[],"type":"function"}];
var Namereg = web3.eth.contract(nameregAbi);
var Deposit = web3.eth.contract(depositAbi);
var namereg = web3.eth.namereg;
var deposit;
var iban;
function validateNamereg() {
var address = document.getElementById('namereg').value;
var ok = /^(0x)?[0-9a-f]{40}$/.test(address) || address === 'default';
if (ok) {
namereg = address === 'default' ? web3.eth.namereg : Namereg.at(address);
document.getElementById('nameregValidation').innerText = 'ok!';
} else {
document.getElementById('nameregValidation').innerText = 'namereg address is incorrect!';
}
return ok;
};
function onNameregKeyUp() {
updateIBAN(validateNamereg());
onExchangeKeyUp();
};
function validateExchange() {
var exchange = document.getElementById('exchange').value;
var ok = /^[0-9A-Z]{4}$/.test(exchange);
if (ok) {
var address = namereg.addr(exchange);
deposit = Deposit.at(address);
document.getElementById('exchangeValidation').innerText = 'ok! address of exchange: ' + address;
} else {
document.getElementById('exchangeValidation').innerText = 'exchange id is incorrect';
}
return ok;
};
function onExchangeKeyUp() {
updateIBAN(validateExchange());
};
function validateClient() {
var client = document.getElementById('client').value;
var ok = /^[0-9A-Z]{9}$/.test(client);
if (ok) {
document.getElementById('clientValidation').innerText = 'ok!';
} else {
document.getElementById('clientValidation').innerText = 'client id is incorrect';
}
return ok;
};
function onClientKeyUp() {
updateIBAN(validateClient());
};
function validateValue() {
try {
var value = document.getElementById('value').value;
var bnValue = new BigNumber(value);
document.getElementById('valueValidation').innerText = bnValue.toString(10);
return true;
} catch (err) {
document.getElementById('valueValidation').innerText = 'Value is incorrect, cannot parse';
return false;
}
};
function onValueKeyUp() {
validateValue();
};
function validateIBAN() {
if (!web3.isIBAN(iban)) {
return document.getElementById('ibanValidation').innerText = ' - IBAN number is incorrect';
}
document.getElementById('ibanValidation').innerText = ' - IBAN number correct';
};
function updateIBAN(ok) {
var exchangeId = document.getElementById('exchange').value;
var clientId = document.getElementById('client').value;
iban = 'XE' + '00' + 'ETH' + exchangeId + clientId;
document.getElementById('iban').innerText = iban;
validateIBAN();
};
function transfer() {
var value = new BigNumber(document.getElementById('value').value);
var exchange = document.getElementById('exchange').value;
var client = document.getElementById('client').value;
deposit.deposit(client, {value: value});
displayTransfer("deposited client's " + client + " funds " + value.toString(10) + " to exchange " + exchange);
};
function displayTransfer(text) {
var node = document.createElement('li');
var textnode = document.createTextNode(text);
node.appendChild(textnode);
document.getElementById('transfers').appendChild(node);
}
</script>
</head>
<body>
<h1>ICAP transfer</h1>
<div>
<h4>namereg address</h4>
</div>
<div>
<text>eg. 0x436474facc88948696b371052a1befb801f003ca or 'default')</text>
</div>
<div>
<input type="text" id="namereg" onkeyup='onNameregKeyUp()' value="default"></input>
<text id="nameregValidation"></text>
</div>
<div>
<h4>exchange identifier</h4>
</div>
<div>
<text>eg. WYWY</text>
</div>
<div>
<input type="text" id="exchange" onkeyup='onExchangeKeyUp()'></input>
<text id="exchangeValidation"></text>
</div>
<div>
<h4>client identifier</h4>
</div>
<div>
<text>eg. GAVOFYORK</text>
</div>
<div>
<input type="text" id="client" onkeyup='onClientKeyUp()'></input>
<text id="clientValidation"></text>
</div>
<div>
<h4>value</h4>
</div>
<div>
<text>eg. 100</text>
</div>
<div>
<input type="text" id="value" onkeyup='onValueKeyUp()'></input>
<text id="valueValidation"></text>
</div>
<div>&nbsp;</div>
<div>
<text>IBAN: </text>
<text id="iban"></text>
<text id="ibanValidation"></text>
</div>
<div>&nbsp;</div>
<div>
<button id="transfer" type="button" onClick="transfer()">Transfer!</button>
<text id="transferValidation"></text>
</div>
<div>
<h4>transfers</h4>
</div>
<div>
<ul id='transfers'></ul>
</div>
</body>
</html>

102
libjsqrc/ethereumjs/example/namereg.html

@ -0,0 +1,102 @@
<!doctype>
<html>
<head>
<script type="text/javascript" src="../dist/web3.js"></script>
<script type="text/javascript">
var web3 = require('web3');
web3.setProvider(new web3.providers.HttpProvider("http://localhost:8545"));
var from = web3.eth.coinbase;
web3.eth.defaultAccount = from;
window.onload = function () {
var filter = web3.eth.namereg.Changed();
filter.watch(function (err, event) {
// live update all fields
onAddressKeyUp();
onNameKeyUp();
onRegisterOwnerKeyUp();
});
};
function registerOwner() {
var name = document.getElementById('registerOwner').value;
web3.eth.namereg.reserve(name);
document.getElementById('nameAvailability').innerText += ' Registering name in progress, please wait...';
};
function changeAddress() {
var name = document.getElementById('registerOwner').value;
var address = document.getElementById('newAddress').value;
web3.eth.namereg.setAddress(name, address, true);
document.getElementById('currentAddress').innerText += ' Changing address in progress. Please wait.';
};
function onRegisterOwnerKeyUp() {
var name = document.getElementById('registerOwner').value;
var owner = web3.eth.namereg.owner(name)
document.getElementById('currentAddress').innerText = web3.eth.namereg.addr(name);
if (owner !== '0x0000000000000000000000000000000000000000') {
if (owner === from) {
document.getElementById('nameAvailability').innerText = "This name is already owned by you " + owner;
} else {
document.getElementById('nameAvailability').innerText = "This name is not available. It's already registered by " + owner;
}
return;
}
document.getElementById('nameAvailability').innerText = "This name is available. You can register it.";
};
function onAddressKeyUp() {
var address = document.getElementById('address').value;
document.getElementById('nameOf').innerText = web3.eth.namereg.name(address);
};
function onNameKeyUp() {
var name = document.getElementById('name').value;
document.getElementById('addressOf').innerText = web3.eth.namereg.addr(name);
};
</script>
</head>
<body>
<i>This example shows only part of namereg functionalities. Namereg contract is available <a href="https://github.com/ethereum/dapp-bin/blob/master/GlobalRegistrar/contract.sol">here</a>
</i>
<h1>Namereg</h1>
<h3>Search for name</h3>
<div>
<text>Address: </text>
<input type="text" id="address" onkeyup='onAddressKeyUp()'></input>
<text>Name: </text>
<text id="nameOf"></text>
</div>
<h3>Search for address</h3>
<div>
<text>Name: </text>
<input type="text" id="name" onkeyup='onNameKeyUp()'></input>
<text>Address: </text>
<text id="addressOf"></text>
</div>
<h3>Register name</h3>
<div>
<text>Check if name is available: </text>
<input type="text" id="registerOwner" onkeyup='onRegisterOwnerKeyUp()'></input>
<text id='nameAvailability'></text>
</div>
<div>
<button id="registerOwnerButton" type="button" onClick="registerOwner()">Register!</button>
</div>
<h3></h3>
<i>If you own the name, you can also change the address it points to</i>
<div>
<text>Address: </text>
<input type="text" id="newAddress"></input>
<button id="changeAddress" type="button" onClick="changeAddress()">Change address!</button>
<text>Current address :</text>
<text id="currentAddress"></text>
</div>
</body>
</html>

76
libjsqrc/ethereumjs/example/natspec_contract.html

@ -1,76 +0,0 @@
<!doctype>
<html>
<head>
<script type="text/javascript" src="../dist/web3.js"></script>
<script type="text/javascript">
var web3 = require('web3');
web3.setProvider(new web3.providers.HttpProvider());
// solidity source code
var source = "" +
"contract test {\n" +
" /// @notice Will multiply `a` by 7. \n" +
" function multiply(uint a) returns(uint d) {\n" +
" return a * 7;\n" +
" }\n" +
"}\n";
// contract description, this will be autogenerated somehow
var desc = [{
"name": "multiply(uint256)",
"type": "function",
"inputs": [
{
"name": "a",
"type": "uint256"
}
],
"outputs": [
{
"name": "d",
"type": "uint256"
}
]
}];
var contract;
function createExampleContract() {
// hide create button
document.getElementById('create').style.visibility = 'hidden';
document.getElementById('source').innerText = source;
// create contract
var address = web3.eth.sendTransaction({code: web3.eth.solidity(source)});
contract = web3.eth.contract(address, desc);
document.getElementById('call').style.visibility = 'visible';
}
function callExampleContract() {
// this should be generated by ethereum
var param = parseInt(document.getElementById('value').value);
// transaction does not return any result, cause it's not synchronous and we don't know,
// when it will be processed
contract.sendTransaction().multiply(param);
document.getElementById('result').innerText = 'transaction made';
}
</script>
</head>
<body>
<h1>contract</h1>
<div id="source"></div>
<div id='create'>
<button type="button" onClick="createExampleContract();">create example contract</button>
</div>
<div id='call' style='visibility: hidden;'>
<input type="number" id="value"></input>
<button type="button" onClick="callExampleContract()">Call Contract</button>
</div>
<div id="result"></div>
</body>
</html>

2
libjsqrc/ethereumjs/index.js

@ -2,6 +2,8 @@ var web3 = require('./lib/web3');
web3.providers.HttpProvider = require('./lib/web3/httpprovider');
web3.providers.QtSyncProvider = require('./lib/web3/qtsync');
web3.eth.contract = require('./lib/web3/contract');
web3.eth.namereg = require('./lib/web3/namereg');
web3.eth.sendIBANTransaction = require('./lib/web3/transfer');
// dont override global variable
if (typeof window !== 'undefined' && typeof window.web3 === 'undefined') {

6
libjsqrc/ethereumjs/lib/solidity/param.js

@ -72,7 +72,7 @@ SolidityParam.prototype.combine = function (param) {
* @returns {Boolean}
*/
SolidityParam.prototype.isDynamic = function () {
return this.value.length > 64;
return this.value.length > 64 || this.offset !== undefined;
};
/**
@ -188,7 +188,7 @@ SolidityParam.decodeBytes = function (bytes, index) {
var offset = getOffset(bytes, index);
// 2 * , cause we also parse length
return new SolidityParam(bytes.substr(offset * 2, 2 * 64));
return new SolidityParam(bytes.substr(offset * 2, 2 * 64), 0);
};
/**
@ -203,7 +203,7 @@ SolidityParam.decodeArray = function (bytes, index) {
index = index || 0;
var offset = getOffset(bytes, index);
var length = parseInt('0x' + bytes.substr(offset * 2, 64));
return new SolidityParam(bytes.substr(offset * 2, (length + 1) * 64));
return new SolidityParam(bytes.substr(offset * 2, (length + 1) * 64), 0);
};
module.exports = SolidityParam;

10
libjsqrc/ethereumjs/lib/utils/config.js

@ -38,11 +38,19 @@ var BigNumber = require('bignumber.js');
var ETH_UNITS = [
'wei',
'Kwei',
'kwei',
'Mwei',
'Gwei',
'szabo',
'finney',
'femtoether',
'picoether',
'nanoether',
'microether',
'milliether',
'nano',
'micro',
'milli',
'ether',
'grand',
'Mether',

39
libjsqrc/ethereumjs/lib/utils/sha3.js

@ -0,0 +1,39 @@
/*
This file is part of ethereum.js.
ethereum.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ethereum.js is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file sha3.js
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
*/
var utils = require('./utils');
var sha3 = require('crypto-js/sha3');
module.exports = function (str, isNew) {
if (str.substr(0, 2) === '0x' && !isNew) {
console.warn('requirement of using web3.fromAscii before sha3 is deprecated');
console.warn('new usage: \'web3.sha3("hello")\'');
console.warn('see https://github.com/ethereum/web3.js/pull/205');
console.warn('if you need to hash hex value, you can do \'sha3("0xfff", true)\'');
str = utils.toAscii(str);
}
return sha3(str, {
outputLength: 256
}).toString();
};

85
libjsqrc/ethereumjs/lib/utils/utils.js

@ -36,22 +36,30 @@
var BigNumber = require('bignumber.js');
var unitMap = {
'wei': '1',
'kwei': '1000',
'ada': '1000',
'mwei': '1000000',
'babbage': '1000000',
'gwei': '1000000000',
'shannon': '1000000000',
'szabo': '1000000000000',
'finney': '1000000000000000',
'ether': '1000000000000000000',
'kether': '1000000000000000000000',
'grand': '1000000000000000000000',
'einstein': '1000000000000000000000',
'mether': '1000000000000000000000000',
'gether': '1000000000000000000000000000',
'tether': '1000000000000000000000000000000'
'wei': '1',
'kwei': '1000',
'ada': '1000',
'femtoether': '1000',
'mwei': '1000000',
'babbage': '1000000',
'picoether': '1000000',
'gwei': '1000000000',
'shannon': '1000000000',
'nanoether': '1000000000',
'nano': '1000000000',
'szabo': '1000000000000',
'microether': '1000000000000',
'micro': '1000000000000',
'finney': '1000000000000000',
'milliether': '1000000000000000',
'milli': '1000000000000000',
'ether': '1000000000000000000',
'kether': '1000000000000000000000',
'grand': '1000000000000000000000',
'einstein': '1000000000000000000000',
'mether': '1000000000000000000000000',
'gether': '1000000000000000000000000000',
'tether': '1000000000000000000000000000000'
};
/**
@ -239,13 +247,14 @@ var getValueOfUnit = function (unit) {
* Takes a number of wei and converts it to any other ether unit.
*
* Possible units are:
* - kwei/ada
* - mwei/babbage
* - gwei/shannon
* - szabo
* - finney
* - ether
* - kether/grand/einstein
* SI Short SI Full Effigy Other
* - kwei femtoether ada
* - mwei picoether babbage
* - gwei nanoether shannon nano
* - -- microether szabo micro
* - -- milliether finney milli
* - ether -- --
* - kether einstein grand
* - mether
* - gether
* - tether
@ -265,13 +274,14 @@ var fromWei = function(number, unit) {
* Takes a number of a unit and converts it to wei.
*
* Possible units are:
* - kwei/ada
* - mwei/babbage
* - gwei/shannon
* - szabo
* - finney
* - ether
* - kether/grand/einstein
* SI Short SI Full Effigy Other
* - kwei femtoether ada
* - mwei picoether babbage
* - gwei nanoether shannon nano
* - -- microether szabo micro
* - -- milliether finney milli
* - ether -- --
* - kether einstein grand
* - mether
* - gether
* - tether
@ -447,6 +457,18 @@ var isJson = function (str) {
}
};
/**
* This method should be called to check if string is valid ethereum IBAN number
* Supports direct and indirect IBANs
*
* @method isIBAN
* @param {String}
* @return {Boolean}
*/
var isIBAN = function (iban) {
return /^XE[0-9]{2}(ETH[0-9A-Z]{13}|[0-9A-Z]{30})$/.test(iban);
};
module.exports = {
padLeft: padLeft,
toHex: toHex,
@ -470,6 +492,7 @@ module.exports = {
isObject: isObject,
isBoolean: isBoolean,
isArray: isArray,
isJson: isJson
isJson: isJson,
isIBAN: isIBAN
};

2
libjsqrc/ethereumjs/lib/version.json

@ -1,3 +1,3 @@
{
"version": "0.4.2"
"version": "0.5.0"
}

13
libjsqrc/ethereumjs/lib/web3.js

@ -35,17 +35,9 @@ var utils = require('./utils/utils');
var formatters = require('./web3/formatters');
var RequestManager = require('./web3/requestmanager');
var c = require('./utils/config');
var Method = require('./web3/method');
var Property = require('./web3/property');
var Batch = require('./web3/batch');
var web3Methods = [
new Method({
name: 'sha3',
call: 'web3_sha3',
params: 1
})
];
var sha3 = require('./utils/sha3');
var web3Properties = [
new Property({
@ -130,6 +122,8 @@ web3.toBigNumber = utils.toBigNumber;
web3.toWei = utils.toWei;
web3.fromWei = utils.fromWei;
web3.isAddress = utils.isAddress;
web3.isIBAN = utils.isIBAN;
web3.sha3 = sha3;
web3.createBatch = function () {
return new Batch();
};
@ -156,7 +150,6 @@ Object.defineProperty(web3.eth, 'defaultAccount', {
});
/// setups all api methods
setupMethods(web3, web3Methods);
setupProperties(web3, web3Properties);
setupMethods(web3.net, net.methods);
setupProperties(web3.net, net.properties);

3
libjsqrc/ethereumjs/lib/web3/event.js

@ -24,6 +24,7 @@ var utils = require('../utils/utils');
var coder = require('../solidity/coder');
var web3 = require('../web3');
var formatters = require('./formatters');
var sha3 = require('../utils/sha3');
/**
* This prototype should be used to create event filters
@ -77,7 +78,7 @@ SolidityEvent.prototype.typeName = function () {
* @return {String} event signature
*/
SolidityEvent.prototype.signature = function () {
return web3.sha3(web3.fromAscii(this._name)).slice(2);
return sha3(this._name);
};
/**

31
libjsqrc/ethereumjs/lib/web3/function.js

@ -23,6 +23,7 @@
var web3 = require('../web3');
var coder = require('../solidity/coder');
var utils = require('../utils/utils');
var sha3 = require('../utils/sha3');
/**
* This prototype should be used to call/sendTransaction to solidity functions
@ -69,12 +70,12 @@ SolidityFunction.prototype.toPayload = function (args) {
* @return {String} function signature
*/
SolidityFunction.prototype.signature = function () {
return web3.sha3(web3.fromAscii(this._name)).slice(2, 10);
return sha3(this._name).slice(0, 8);
};
SolidityFunction.prototype.unpackOutput = function (output) {
if (output === null) {
if (!output) {
return;
}
@ -94,7 +95,7 @@ SolidityFunction.prototype.unpackOutput = function (output) {
* @return {String} output bytes
*/
SolidityFunction.prototype.call = function () {
var args = Array.prototype.slice.call(arguments);
var args = Array.prototype.slice.call(arguments).filter(function (a) {return a !== undefined; });
var callback = this.extractCallback(args);
var payload = this.toPayload(args);
@ -116,18 +117,35 @@ SolidityFunction.prototype.call = function () {
* @param {Object} options
*/
SolidityFunction.prototype.sendTransaction = function () {
var args = Array.prototype.slice.call(arguments);
var args = Array.prototype.slice.call(arguments).filter(function (a) {return a !== undefined; });
var callback = this.extractCallback(args);
var payload = this.toPayload(args);
if (!callback) {
web3.eth.sendTransaction(payload);
return;
return web3.eth.sendTransaction(payload);
}
web3.eth.sendTransaction(payload, callback);
};
/**
* Should be used to estimateGas of solidity function
*
* @method estimateGas
* @param {Object} options
*/
SolidityFunction.prototype.estimateGas = function () {
var args = Array.prototype.slice.call(arguments);
var callback = this.extractCallback(args);
var payload = this.toPayload(args);
if (!callback) {
return web3.eth.estimateGas(payload);
}
web3.eth.estimateGas(payload, callback);
};
/**
* Should be used to get function display name
*
@ -195,6 +213,7 @@ SolidityFunction.prototype.attachToContract = function (contract) {
execute.request = this.request.bind(this);
execute.call = this.call.bind(this);
execute.sendTransaction = this.sendTransaction.bind(this);
execute.estimateGas = this.estimateGas.bind(this);
var displayName = this.displayName();
if (!contract[displayName]) {
contract[displayName] = execute;

108
libjsqrc/ethereumjs/lib/web3/icap.js

@ -0,0 +1,108 @@
/*
This file is part of ethereum.js.
ethereum.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ethereum.js is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file icap.js
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
*/
var utils = require('../utils/utils');
/**
* This prototype should be used to extract necessary information from iban address
*
* @param {String} iban
*/
var ICAP = function (iban) {
this._iban = iban;
};
/**
* Should be called to check if icap is correct
*
* @method isValid
* @returns {Boolean} true if it is, otherwise false
*/
ICAP.prototype.isValid = function () {
return utils.isIBAN(this._iban);
};
/**
* Should be called to check if iban number is direct
*
* @method isDirect
* @returns {Boolean} true if it is, otherwise false
*/
ICAP.prototype.isDirect = function () {
return this._iban.length === 34;
};
/**
* Should be called to check if iban number if indirect
*
* @method isIndirect
* @returns {Boolean} true if it is, otherwise false
*/
ICAP.prototype.isIndirect = function () {
return this._iban.length === 20;
};
/**
* Should be called to get iban checksum
* Uses the mod-97-10 checksumming protocol (ISO/IEC 7064:2003)
*
* @method checksum
* @returns {String} checksum
*/
ICAP.prototype.checksum = function () {
return this._iban.substr(2, 2);
};
/**
* Should be called to get institution identifier
* eg. XREG
*
* @method institution
* @returns {String} institution identifier
*/
ICAP.prototype.institution = function () {
return this.isIndirect() ? this._iban.substr(7, 4) : '';
};
/**
* Should be called to get client identifier within institution
* eg. GAVOFYORK
*
* @method client
* @returns {String} client identifier
*/
ICAP.prototype.client = function () {
return this.isIndirect() ? this._iban.substr(11) : '';
};
/**
* Should be called to get client direct address
*
* @method address
* @returns {String} client direct address
*/
ICAP.prototype.address = function () {
return this.isDirect() ? this._iban.substr(4) : '';
};
module.exports = ICAP;

46
libjsqrc/ethereumjs/lib/web3/namereg.js

@ -0,0 +1,46 @@
/*
This file is part of ethereum.js.
ethereum.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ethereum.js is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file namereg.js
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
*/
var contract = require('./contract');
var address = '0xc6d9d2cd449a754c494264e1809c50e34d64562b';
var abi = [
{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"name","outputs":[{"name":"o_name","type":"bytes32"}],"type":"function"},
{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"owner","outputs":[{"name":"","type":"address"}],"type":"function"},
{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"content","outputs":[{"name":"","type":"bytes32"}],"type":"function"},
{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"addr","outputs":[{"name":"","type":"address"}],"type":"function"},
{"constant":false,"inputs":[{"name":"_name","type":"bytes32"}],"name":"reserve","outputs":[],"type":"function"},
{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"subRegistrar","outputs":[{"name":"o_subRegistrar","type":"address"}],"type":"function"},
{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_newOwner","type":"address"}],"name":"transfer","outputs":[],"type":"function"},
{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_registrar","type":"address"}],"name":"setSubRegistrar","outputs":[],"type":"function"},
{"constant":false,"inputs":[],"name":"Registrar","outputs":[],"type":"function"},
{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_a","type":"address"},{"name":"_primary","type":"bool"}],"name":"setAddress","outputs":[],"type":"function"},
{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_content","type":"bytes32"}],"name":"setContent","outputs":[],"type":"function"},
{"constant":false,"inputs":[{"name":"_name","type":"bytes32"}],"name":"disown","outputs":[],"type":"function"},
{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"register","outputs":[{"name":"","type":"address"}],"type":"function"},
{"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"bytes32"}],"name":"Changed","type":"event"},
{"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"bytes32"},{"indexed":true,"name":"addr","type":"address"}],"name":"PrimaryChanged","type":"event"}
];
module.exports = contract(abi).at(address);

94
libjsqrc/ethereumjs/lib/web3/transfer.js

@ -0,0 +1,94 @@
/*
This file is part of ethereum.js.
ethereum.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ethereum.js is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file transfer.js
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
*/
var web3 = require('../web3');
var ICAP = require('./icap');
var namereg = require('./namereg');
var contract = require('./contract');
/**
* Should be used to make ICAP transfer
*
* @method transfer
* @param {String} iban number
* @param {String} from (address)
* @param {Value} value to be tranfered
* @param {Function} callback, callback
*/
var transfer = function (from, iban, value, callback) {
var icap = new ICAP(iban);
if (!icap.isValid()) {
throw new Error('invalid iban address');
}
if (icap.isDirect()) {
return transferToAddress(from, icap.address(), value, callback);
}
if (!callback) {
var address = namereg.addr(icap.institution());
return deposit(from, address, value, icap.client());
}
namereg.addr(icap.insitution(), function (err, address) {
return deposit(from, address, value, icap.client(), callback);
});
};
/**
* Should be used to transfer funds to certain address
*
* @method transferToAddress
* @param {String} address
* @param {String} from (address)
* @param {Value} value to be tranfered
* @param {Function} callback, callback
*/
var transferToAddress = function (from, address, value, callback) {
return web3.eth.sendTransaction({
address: address,
from: from,
value: value
}, callback);
};
/**
* Should be used to deposit funds to generic Exchange contract (must implement deposit(bytes32) method!)
*
* @method deposit
* @param {String} address
* @param {String} from (address)
* @param {Value} value to be tranfered
* @param {String} client unique identifier
* @param {Function} callback, callback
*/
var deposit = function (from, address, value, client, callback) {
var abi = [{"constant":false,"inputs":[{"name":"name","type":"bytes32"}],"name":"deposit","outputs":[],"type":"function"}];
return contract(abi).at(address).deposit(client, {
from: from,
value: value
}, callback);
};
module.exports = transfer;

2
libjsqrc/ethereumjs/package.js

@ -1,7 +1,7 @@
/* jshint ignore:start */
Package.describe({
name: 'ethereum:web3',
version: '0.4.2',
version: '0.5.0',
summary: 'Ethereum JavaScript API, middleware to talk to a ethreum node over RPC',
git: 'https://github.com/ethereum/ethereum.js',
// By default, Meteor will default to using README.md for documentation.

3
libjsqrc/ethereumjs/package.json

@ -1,7 +1,7 @@
{
"name": "web3",
"namespace": "ethereum",
"version": "0.4.2",
"version": "0.5.0",
"description": "Ethereum JavaScript API, middleware to talk to a ethereum node over RPC",
"main": "./index.js",
"directories": {
@ -9,6 +9,7 @@
},
"dependencies": {
"bignumber.js": "debris/bignumber.js#master",
"crypto-js": "^3.1.4",
"xmlhttprequest": "*"
},
"browser": {

4
libjsqrc/ethereumjs/test/batch.js

@ -58,10 +58,6 @@ describe('lib/web3/batch', function () {
var address = '0x0000000000000000000000000000000000000000';
var result = '0x126';
var result2 = '0x0000000000000000000000000000000000000000000000000000000000000123';
var signature = '0x001122334455';
// TODO: fix this, maybe in browser sha3?
provider.injectResult(signature);
var counter = 0;
var callback = function (err, r) {

2
libjsqrc/ethereumjs/test/coder.decodeParam.js

@ -24,6 +24,8 @@ describe('lib/solidity/coder', function () {
test({ type: 'bytes', expected: 'gavofyork', value: '0000000000000000000000000000000000000000000000000000000000000020' +
'0000000000000000000000000000000000000000000000000000000000000009' +
'6761766f66796f726b0000000000000000000000000000000000000000000000'});
test({ type: 'int[]', expected: [], value: '0000000000000000000000000000000000000000000000000000000000000020' +
'0000000000000000000000000000000000000000000000000000000000000000'});
test({ type: 'int[]', expected: [new bn(3)], value: '0000000000000000000000000000000000000000000000000000000000000020' +
'0000000000000000000000000000000000000000000000000000000000000001' +
'0000000000000000000000000000000000000000000000000000000000000003'});

2
libjsqrc/ethereumjs/test/coder.encodeParam.js

@ -24,6 +24,8 @@ describe('lib/solidity/coder', function () {
test({ type: 'bytes', value: 'gavofyork', expected: '0000000000000000000000000000000000000000000000000000000000000020' +
'0000000000000000000000000000000000000000000000000000000000000009' +
'6761766f66796f726b0000000000000000000000000000000000000000000000'});
test({ type: 'int[]', value: [], expected: '0000000000000000000000000000000000000000000000000000000000000020' +
'0000000000000000000000000000000000000000000000000000000000000000'});
test({ type: 'int[]', value: [3], expected: '0000000000000000000000000000000000000000000000000000000000000020' +
'0000000000000000000000000000000000000000000000000000000000000001' +
'0000000000000000000000000000000000000000000000000000000000000003'});

308
libjsqrc/ethereumjs/test/contract.js

@ -5,6 +5,7 @@ var FakeHttpProvider = require('./helpers/FakeHttpProvider');
var FakeHttpProvider2 = require('./helpers/FakeHttpProvider2');
var utils = require('../lib/utils/utils');
var BigNumber = require('bignumber.js');
var sha3 = require('../lib/utils/sha3');
var desc = [{
"name": "balance(address)",
@ -60,34 +61,28 @@ describe('web3.eth.contract', function () {
var provider = new FakeHttpProvider();
web3.setProvider(provider);
web3.reset(); // reset different polls
var sha3 = '0x5131231231231231231231';
provider.injectResult(sha3);
var signature = 'Changed(address,uint256,uint256,uint256)';
var step = 0;
provider.injectValidation(function (payload) {
if (step === 0) {
step = 1;
assert.equal(payload.jsonrpc, '2.0');
assert.equal(payload.method, 'web3_sha3');
assert.equal(payload.params[0], web3.fromAscii('Changed(address,uint256,uint256,uint256)'));
} else if (step === 1) {
step = 2;
provider.injectResult(3);
assert.equal(payload.jsonrpc, '2.0');
assert.equal(payload.method, 'eth_newFilter');
assert.deepEqual(payload.params[0], {
topics: [
sha3,
'0x' + sha3(signature),
'0x0000000000000000000000001234567890123456789012345678901234567890',
null
],
address: '0x1234567890123456789012345678901234567890'
});
} else if (step === 2) {
step = 3;
} else if (step === 1) {
step = 2;
provider.injectResult([{
address: address,
topics: [
sha3,
'0x' + sha3(signature),
'0x0000000000000000000000001234567890123456789012345678901234567890',
'0x0000000000000000000000000000000000000000000000000000000000000001'
],
@ -97,11 +92,11 @@ describe('web3.eth.contract', function () {
}]);
assert.equal(payload.jsonrpc, '2.0');
assert.equal(payload.method, 'eth_getFilterLogs');
} else if (step === 3 && utils.isArray(payload)) {
} else if (step === 2 && utils.isArray(payload)) {
provider.injectBatchResults([[{
address: address,
topics: [
sha3,
'0x' + sha3(signature),
'0x0000000000000000000000001234567890123456789012345678901234567890',
'0x0000000000000000000000000000000000000000000000000000000000000001'
],
@ -135,53 +130,37 @@ describe('web3.eth.contract', function () {
var provider = new FakeHttpProvider();
web3.setProvider(provider);
web3.reset();
var sha3 = '0x5131231231231231231231';
provider.injectResult('0x0000000000000000000000000000000000000000000000000000000000000032');
var signature = 'balance(address)'
var address = '0x1234567890123456789012345678901234567890';
provider.injectResult(sha3);
var step = 0;
provider.injectValidation(function (payload) {
if (step === 0) {
step = 1;
assert.equal(payload.jsonrpc, '2.0');
assert.equal(payload.method, 'web3_sha3');
assert.equal(payload.params[0], web3.fromAscii('balance(address)'));
} else if (step === 1) {
assert.equal(payload.method, 'eth_call');
assert.deepEqual(payload.params, [{
data: sha3.slice(0, 10) + '0000000000000000000000001234567890123456789012345678901234567890',
to: address
}, 'latest']);
}
assert.equal(payload.method, 'eth_call');
assert.deepEqual(payload.params, [{
data: '0x' + sha3(signature).slice(0, 8) + '0000000000000000000000001234567890123456789012345678901234567890',
to: address
}, 'latest']);
});
var contract = web3.eth.contract(desc).at(address);
contract.balance(address);
var r = contract.balance(address);
assert.deepEqual(new BigNumber(0x32), r);
});
it('should sendTransaction to contract function', function () {
var provider = new FakeHttpProvider();
web3.setProvider(provider);
web3.reset();
var sha3 = '0x5131231231231231231231';
var signature = 'send(address,uint256)';
var address = '0x1234567890123456789012345678901234567890';
provider.injectResult(sha3);
var step = 0;
provider.injectValidation(function (payload) {
if (step === 0) {
step = 1;
assert.equal(payload.jsonrpc, '2.0');
assert.equal(payload.method, 'web3_sha3');
assert.equal(payload.params[0], web3.fromAscii('send(address,uint256)'));
} else if (step === 1) {
assert.equal(payload.method, 'eth_sendTransaction');
assert.deepEqual(payload.params, [{
data: sha3.slice(0, 10) +
'0000000000000000000000001234567890123456789012345678901234567890' +
'0000000000000000000000000000000000000000000000000000000000000011' ,
to: address
}]);
}
assert.equal(payload.method, 'eth_sendTransaction');
assert.deepEqual(payload.params, [{
data: '0x' + sha3(signature).slice(0, 8) +
'0000000000000000000000001234567890123456789012345678901234567890' +
'0000000000000000000000000000000000000000000000000000000000000011' ,
to: address
}]);
});
var contract = web3.eth.contract(desc).at(address);
@ -194,30 +173,23 @@ describe('web3.eth.contract', function () {
var provider = new FakeHttpProvider();
web3.setProvider(provider);
web3.reset();
var sha3 = '0x5131231231231231231231';
provider.injectResult('0x0000000000000000000000000000000000000000000000000000000000000032');
var signature = 'balance(address)';
var address = '0x1234567890123456789012345678901234567890';
provider.injectResult(sha3);
var step = 0;
provider.injectValidation(function (payload) {
if (step === 0) {
step = 1;
assert.equal(payload.jsonrpc, '2.0');
assert.equal(payload.method, 'web3_sha3');
assert.equal(payload.params[0], web3.fromAscii('balance(address)'));
} else if (step === 1) {
assert.equal(payload.method, 'eth_call');
assert.deepEqual(payload.params, [{
data: sha3.slice(0, 10) + '0000000000000000000000001234567890123456789012345678901234567890',
to: address,
from: address,
gas: '0xc350'
}, 'latest']);
}
assert.equal(payload.method, 'eth_call');
assert.deepEqual(payload.params, [{
data: '0x' + sha3(signature).slice(0, 8) + '0000000000000000000000001234567890123456789012345678901234567890',
to: address,
from: address,
gas: '0xc350'
}, 'latest']);
});
var contract = web3.eth.contract(desc).at(address);
contract.balance(address, {from: address, gas: 50000});
var r = contract.balance(address, {from: address, gas: 50000});
assert.deepEqual(new BigNumber(0x32), r);
});
@ -226,30 +198,23 @@ describe('web3.eth.contract', function () {
var provider = new FakeHttpProvider();
web3.setProvider(provider);
web3.reset();
var sha3 = '0x5131231231231231231231';
provider.injectResult('0x0000000000000000000000000000000000000000000000000000000000000032');
var signature = 'balance(address)';
var address = '0x1234567890123456789012345678901234567890';
provider.injectResult(sha3);
var step = 0;
provider.injectValidation(function (payload) {
if (step === 0) {
step = 1;
assert.equal(payload.jsonrpc, '2.0');
assert.equal(payload.method, 'web3_sha3');
assert.equal(payload.params[0], web3.fromAscii('balance(address)'));
} else if (step === 1) {
assert.equal(payload.method, 'eth_call');
assert.deepEqual(payload.params, [{
data: sha3.slice(0, 10) + '0000000000000000000000001234567890123456789012345678901234567890',
to: address,
from: address,
gas: '0xc350'
}, 'latest']);
}
assert.equal(payload.method, 'eth_call');
assert.deepEqual(payload.params, [{
data: '0x' + sha3(signature).slice(0, 8) + '0000000000000000000000001234567890123456789012345678901234567890',
to: address,
from: address,
gas: '0xc350'
}, 'latest']);
});
var contract = web3.eth.contract(desc).at(address);
contract.balance.call(address, {from: address, gas: 50000});
var r = contract.balance.call(address, {from: address, gas: 50000});
assert.deepEqual(new BigNumber(0x32), r);
});
@ -257,29 +222,20 @@ describe('web3.eth.contract', function () {
var provider = new FakeHttpProvider();
web3.setProvider(provider);
web3.reset();
var sha3 = '0x5131231231231231231231';
var signature = 'send(address,uint256)';
var address = '0x1234567890123456789012345678901234567890';
provider.injectResult(sha3);
var step = 0;
provider.injectValidation(function (payload) {
if (step === 0) {
step = 1;
assert.equal(payload.jsonrpc, '2.0');
assert.equal(payload.method, 'web3_sha3');
assert.equal(payload.params[0], web3.fromAscii('send(address,uint256)'));
} else if (step === 1) {
assert.equal(payload.method, 'eth_sendTransaction');
assert.deepEqual(payload.params, [{
data: sha3.slice(0, 10) +
'0000000000000000000000001234567890123456789012345678901234567890' +
'0000000000000000000000000000000000000000000000000000000000000011' ,
to: address,
from: address,
gas: '0xc350',
gasPrice: '0xbb8',
value: '0x2710'
}]);
}
assert.equal(payload.method, 'eth_sendTransaction');
assert.deepEqual(payload.params, [{
data: '0x' + sha3(signature).slice(0, 8) +
'0000000000000000000000001234567890123456789012345678901234567890' +
'0000000000000000000000000000000000000000000000000000000000000011' ,
to: address,
from: address,
gas: '0xc350',
gasPrice: '0xbb8',
value: '0x2710'
}]);
});
var contract = web3.eth.contract(desc).at(address);
@ -291,29 +247,20 @@ describe('web3.eth.contract', function () {
var provider = new FakeHttpProvider();
web3.setProvider(provider);
web3.reset();
var sha3 = '0x5131231231231231231231';
var signature = 'send(address,uint256)';
var address = '0x1234567890123456789012345678901234567890';
provider.injectResult(sha3);
var step = 0;
provider.injectValidation(function (payload) {
if (step === 0) {
step = 1;
assert.equal(payload.jsonrpc, '2.0');
assert.equal(payload.method, 'web3_sha3');
assert.equal(payload.params[0], web3.fromAscii('send(address,uint256)'));
} else if (step === 1) {
assert.equal(payload.method, 'eth_sendTransaction');
assert.deepEqual(payload.params, [{
data: sha3.slice(0, 10) +
'0000000000000000000000001234567890123456789012345678901234567890' +
'0000000000000000000000000000000000000000000000000000000000000011' ,
to: address,
from: address,
gas: '0xc350',
gasPrice: '0xbb8',
value: '0x2710'
}]);
}
assert.equal(payload.method, 'eth_sendTransaction');
assert.deepEqual(payload.params, [{
data: '0x' + sha3(signature).slice(0, 8) +
'0000000000000000000000001234567890123456789012345678901234567890' +
'0000000000000000000000000000000000000000000000000000000000000011' ,
to: address,
from: address,
gas: '0xc350',
gasPrice: '0xbb8',
value: '0x2710'
}]);
});
var contract = web3.eth.contract(desc).at(address);
@ -325,29 +272,20 @@ describe('web3.eth.contract', function () {
var provider = new FakeHttpProvider();
web3.setProvider(provider);
web3.reset();
var sha3 = '0x5131231231231231231231';
var address = '0x1234567890123456789012345678901234567890';
provider.injectResult(sha3);
var step = 0;
var signature = 'send(address,uint256)';
provider.injectValidation(function (payload) {
if (step === 0) {
step = 1;
assert.equal(payload.jsonrpc, '2.0');
assert.equal(payload.method, 'web3_sha3');
assert.equal(payload.params[0], web3.fromAscii('send(address,uint256)'));
} else if (step === 1) {
assert.equal(payload.method, 'eth_sendTransaction');
assert.deepEqual(payload.params, [{
data: sha3.slice(0, 10) +
'0000000000000000000000001234567890123456789012345678901234567890' +
'0000000000000000000000000000000000000000000000000000000000000011' ,
to: address,
from: address,
gas: '0xc350',
gasPrice: '0xbb8',
value: '0x2710'
}]);
}
assert.equal(payload.method, 'eth_sendTransaction');
assert.deepEqual(payload.params, [{
data: '0x' + sha3(signature).slice(0, 8) +
'0000000000000000000000001234567890123456789012345678901234567890' +
'0000000000000000000000000000000000000000000000000000000000000011' ,
to: address,
from: address,
gas: '0xc350',
gasPrice: '0xbb8',
value: '0x2710'
}]);
});
var contract = web3.eth.contract(desc).at(address);
@ -358,32 +296,52 @@ describe('web3.eth.contract', function () {
});
});
it('should explicitly estimateGas with optional params', function () {
var provider = new FakeHttpProvider();
web3.setProvider(provider);
web3.reset();
var signature = 'send(address,uint256)';
var address = '0x1234567890123456789012345678901234567890';
provider.injectValidation(function (payload) {
assert.equal(payload.method, 'eth_estimateGas');
assert.deepEqual(payload.params, [{
data: '0x' + sha3(signature).slice(0, 8) +
'0000000000000000000000001234567890123456789012345678901234567890' +
'0000000000000000000000000000000000000000000000000000000000000011' ,
to: address,
from: address,
gas: '0xc350',
gasPrice: '0xbb8',
value: '0x2710'
}]);
});
var contract = web3.eth.contract(desc).at(address);
contract.send.estimateGas(address, 17, {from: address, gas: 50000, gasPrice: 3000, value: 10000});
});
it('should call testArr method and properly parse result', function () {
var provider = new FakeHttpProvider2();
web3.setProvider(provider);
web3.reset();
var sha3 = '0x5131231231231231231231';
var signature = 'testArr(int[])';
var address = '0x1234567890123456789012345678901234567890';
provider.injectResultList([{
result: sha3
}, {
result: '0x0000000000000000000000000000000000000000000000000000000000000005'
}]);
var step = 0;
provider.injectValidation(function (payload) {
if (step === 1) { // getting sha3 is first
assert.equal(payload.method, 'eth_call');
assert.deepEqual(payload.params, [{
data: sha3.slice(0, 10) +
'0000000000000000000000000000000000000000000000000000000000000020' +
'0000000000000000000000000000000000000000000000000000000000000001' +
'0000000000000000000000000000000000000000000000000000000000000003',
to: address
},
'latest'
assert.equal(payload.method, 'eth_call');
assert.deepEqual(payload.params, [{
data: '0x' + sha3(signature).slice(0, 8) +
'0000000000000000000000000000000000000000000000000000000000000020' +
'0000000000000000000000000000000000000000000000000000000000000001' +
'0000000000000000000000000000000000000000000000000000000000000003',
to: address
},
'latest'
]);
}
step++;
});
var contract = web3.eth.contract(desc).at(address);
@ -396,28 +354,22 @@ describe('web3.eth.contract', function () {
var provider = new FakeHttpProvider2();
web3.setProvider(provider);
web3.reset();
var sha3 = '0x5131231231231231231231';
var signature = 'testArr(int[])';
var address = '0x1234567890123456789012345678901234567890';
provider.injectResultList([{
result: sha3
}, {
result: '0x0000000000000000000000000000000000000000000000000000000000000005'
}]);
var step = 0;
provider.injectValidation(function (payload) {
if (step === 1) { // getting sha3 is first
assert.equal(payload.method, 'eth_call');
assert.deepEqual(payload.params, [{
data: sha3.slice(0, 10) +
'0000000000000000000000000000000000000000000000000000000000000020' +
'0000000000000000000000000000000000000000000000000000000000000001' +
'0000000000000000000000000000000000000000000000000000000000000003',
to: address
},
'latest'
]);
}
step++;
assert.equal(payload.method, 'eth_call');
assert.deepEqual(payload.params, [{
data: '0x' + sha3(signature).slice(0, 8) +
'0000000000000000000000000000000000000000000000000000000000000020' +
'0000000000000000000000000000000000000000000000000000000000000001' +
'0000000000000000000000000000000000000000000000000000000000000003',
to: address
},
'latest'
]);
});
var contract = web3.eth.contract(desc).at(address);

17
libjsqrc/ethereumjs/test/sha3.js

@ -0,0 +1,17 @@
var chai = require('chai');
var assert = chai.assert;
var sha3 = require('../lib/utils/sha3');
var web3 = require('../index');
describe('lib/utils/sha3', function () {
var test = function (v, e) {
it('should encode ' + v + ' to ' + e, function () {
assert.equal(sha3(v), e);
});
};
test('test123', 'f81b517a242b218999ec8eec0ea6e2ddbef2a367a14e93f4a32a39e260f686ad');
test('test(int)', 'f4d03772bec1e62fbe8c5691e1a9101e520e8f8b5ca612123694632bf3cb51b1');
test(web3.fromAscii('test123'), 'f81b517a242b218999ec8eec0ea6e2ddbef2a367a14e93f4a32a39e260f686ad');
});

32
libjsqrc/ethereumjs/test/utils.isIBAN.js

@ -0,0 +1,32 @@
var chai = require('chai');
var utils = require('../lib/utils/utils.js');
var assert = chai.assert;
var tests = [
{ obj: function () {}, is: false},
{ obj: new Function(), is: false},
{ obj: 'function', is: false},
{ obj: {}, is: false},
{ obj: '[]', is: false},
{ obj: '[1, 2]', is: false},
{ obj: '{}', is: false},
{ obj: '{"a": 123, "b" :3,}', is: false},
{ obj: '{"c" : 2}', is: false},
{ obj: 'XE81ETHXREGGAVOFYORK', is: true},
{ obj: 'XE81ETCXREGGAVOFYORK', is: false},
{ obj: 'XE81ETHXREGGAVOFYORKD', is: false},
{ obj: 'XE81ETHXREGGaVOFYORK', is: false},
{ obj: 'XE7338O073KYGTWWZN0F2WZ0R8PX5ZPPZS', is: true},
{ obj: 'XD7338O073KYGTWWZN0F2WZ0R8PX5ZPPZS', is: false}
];
describe('lib/utils/utils', function () {
describe('isIBAN', function () {
tests.forEach(function (test) {
it('shoud test if value ' + test.obj + ' is iban: ' + test.is, function () {
assert.equal(utils.isIBAN(test.obj), test.is);
});
});
});
});

8
libjsqrc/ethereumjs/test/utils.toWei.js

@ -19,6 +19,14 @@ describe('lib/utils/utils', function () {
assert.equal(utils.toWei(1, 'gether'), '1000000000000000000000000000');
assert.equal(utils.toWei(1, 'tether'), '1000000000000000000000000000000');
assert.equal(utils.toWei(1, 'kwei'), utils.toWei(1, 'femtoether'));
assert.equal(utils.toWei(1, 'babbage'), utils.toWei(1, 'picoether'));
assert.equal(utils.toWei(1, 'shannon'), utils.toWei(1, 'nanoether'));
assert.equal(utils.toWei(1, 'szabo'), utils.toWei(1, 'microether'));
assert.equal(utils.toWei(1, 'finney'), utils.toWei(1, 'milliether'));
assert.equal(utils.toWei(1, 'milli'), utils.toWei(1, 'milliether'));
assert.equal(utils.toWei(1, 'milli'), utils.toWei(1000, 'micro'));
assert.throws(function () {utils.toWei(1, 'wei1');}, Error);
});
});

49
libjsqrc/ethereumjs/test/web3.eth.sendIBANTransaction.js

@ -0,0 +1,49 @@
var chai = require('chai');
var assert = chai.assert;
var web3 = require('../index');
var FakeHttpProvider = require('./helpers/FakeHttpProvider');
var FakeHttpProvider2 = require('./helpers/FakeHttpProvider2');
describe('web3.eth.sendIBANTransaction', function () {
it('should send transaction', function () {
var iban = 'XE81ETHXREGGAVOFYORK';
var address = '0x1234567890123456789012345678901234500000';
var exAddress = '0x1234567890123456789012345678901234567890'
var provider = new FakeHttpProvider2();
web3.setProvider(provider);
web3.reset();
provider.injectResultList([{
result: exAddress
}, {
result: ''
}]);
var step = 0;
provider.injectValidation(function (payload) {
if (step === 0) {
step++;
assert.equal(payload.method, 'eth_call');
assert.deepEqual(payload.params, [{
data: '0x3b3b57de5852454700000000000000000000000000000000000000000000000000000000',
to: web3.eth.namereg.address
}, "latest"]);
return;
}
assert.equal(payload.method, 'eth_sendTransaction');
assert.deepEqual(payload.params, [{
data: '0xb214faa54741564f46594f524b0000000000000000000000000000000000000000000000',
from: address,
to: exAddress,
value: payload.params[0].value // don't check this
}]);
});
web3.eth.sendIBANTransaction(address, iban, 10000);
});
});

16
libjsqrc/ethereumjs/test/web3.sha3.js

@ -1,16 +0,0 @@
var BigNumber = require('bignumber.js');
var web3 = require('../index');
var testMethod = require('./helpers/test.method.js');
var method = 'sha3';
var tests = [{
args: ['myString'],
formattedArgs: ['myString'],
result: '0x319319f831983198319881',
formattedResult: '0x319319f831983198319881',
call: 'web3_'+ method
}];
testMethod.runTests(null, method, tests);

3
libsolidity/AST.cpp

@ -469,9 +469,6 @@ void FunctionDefinition::checkTypeRequirements()
{
if (!var->getType()->canLiveOutsideStorage())
BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage."));
// todo delete when will be implemented arrays as parameter type in internal functions
if (getVisibility() == Visibility::Public && var->getType()->getCategory() == Type::Category::Array)
BOOST_THROW_EXCEPTION(var->createTypeError("Arrays only implemented for external functions."));
if (getVisibility() >= Visibility::Public && !(var->getType()->externalType()))
BOOST_THROW_EXCEPTION(var->createTypeError("Internal type is not allowed for public and external functions."));
}

71
libsolidity/Compiler.cpp

@ -52,21 +52,27 @@ void Compiler::compileContract(ContractDefinition const& _contract,
map<ContractDefinition const*, bytes const*> const& _contracts)
{
m_context = CompilerContext(); // clear it just in case
initializeContext(_contract, _contracts);
appendFunctionSelector(_contract);
set<Declaration const*> functions = m_context.getFunctionsWithoutCode();
while (!functions.empty())
{
for (Declaration const* function: functions)
CompilerContext::LocationSetter locationSetterRunTime(m_context, _contract);
CompilerUtils(m_context).initialiseFreeMemoryPointer();
initializeContext(_contract, _contracts);
appendFunctionSelector(_contract);
set<Declaration const*> functions = m_context.getFunctionsWithoutCode();
while (!functions.empty())
{
m_context.setStackOffset(0);
function->accept(*this);
for (Declaration const* function: functions)
{
m_context.setStackOffset(0);
function->accept(*this);
}
functions = m_context.getFunctionsWithoutCode();
}
functions = m_context.getFunctionsWithoutCode();
}
// Swap the runtime context with the creation-time context
swap(m_context, m_runtimeContext);
CompilerContext::LocationSetter locationSetterCreationTime(m_context, _contract);
CompilerUtils(m_context).initialiseFreeMemoryPointer();
initializeContext(_contract, _contracts);
packIntoContractCreator(_contract, m_runtimeContext);
if (m_optimize)
@ -233,31 +239,42 @@ void Compiler::appendCalldataUnpacker(TypePointers const& _typeParameters, bool
m_context << u256(CompilerUtils::dataStartOffset);
for (TypePointer const& type: _typeParameters)
{
switch (type->getCategory())
if (type->getCategory() == Type::Category::Array)
{
case Type::Category::Array:
if (type->isDynamicallySized())
auto const& arrayType = dynamic_cast<ArrayType const&>(*type);
if (arrayType.location() == ReferenceType::Location::CallData)
{
// put on stack: data_pointer length
CompilerUtils(m_context).loadFromMemoryDynamic(IntegerType(256), !_fromMemory);
// stack: data_offset next_pointer
//@todo once we support nested arrays, this offset needs to be dynamic.
m_context << eth::Instruction::SWAP1 << u256(CompilerUtils::dataStartOffset);
m_context << eth::Instruction::ADD;
// stack: next_pointer data_pointer
// retrieve length
CompilerUtils(m_context).loadFromMemoryDynamic(IntegerType(256), !_fromMemory, true);
// stack: next_pointer length data_pointer
m_context << eth::Instruction::SWAP2;
if (type->isDynamicallySized())
{
// put on stack: data_pointer length
CompilerUtils(m_context).loadFromMemoryDynamic(IntegerType(256), !_fromMemory);
// stack: data_offset next_pointer
//@todo once we support nested arrays, this offset needs to be dynamic.
m_context << eth::Instruction::SWAP1 << u256(CompilerUtils::dataStartOffset);
m_context << eth::Instruction::ADD;
// stack: next_pointer data_pointer
// retrieve length
CompilerUtils(m_context).loadFromMemoryDynamic(IntegerType(256), !_fromMemory, true);
// stack: next_pointer length data_pointer
m_context << eth::Instruction::SWAP2;
}
else
{
// leave the pointer on the stack
m_context << eth::Instruction::DUP1;
m_context << u256(type->getCalldataEncodedSize()) << eth::Instruction::ADD;
}
}
else
{
// leave the pointer on the stack
m_context << eth::Instruction::DUP1;
m_context << u256(type->getCalldataEncodedSize()) << eth::Instruction::ADD;
solAssert(arrayType.location() == ReferenceType::Location::Memory, "");
CompilerUtils(m_context).fetchFreeMemoryPointer();
CompilerUtils(m_context).storeInMemoryDynamic(*type);
CompilerUtils(m_context).storeFreeMemoryPointer();
}
break;
default:
}
else
{
solAssert(!type->isDynamicallySized(), "Unknown dynamically sized type: " + type->toString());
CompilerUtils(m_context).loadFromMemoryDynamic(*type, !_fromMemory, true);
}

27
libsolidity/CompilerUtils.cpp

@ -31,7 +31,31 @@ namespace dev
namespace solidity
{
const unsigned int CompilerUtils::dataStartOffset = 4;
const unsigned CompilerUtils::dataStartOffset = 4;
const size_t CompilerUtils::freeMemoryPointer = 64;
void CompilerUtils::initialiseFreeMemoryPointer()
{
m_context << u256(freeMemoryPointer + 32);
storeFreeMemoryPointer();
}
void CompilerUtils::fetchFreeMemoryPointer()
{
m_context << u256(freeMemoryPointer) << eth::Instruction::MLOAD;
}
void CompilerUtils::storeFreeMemoryPointer()
{
m_context << u256(freeMemoryPointer) << eth::Instruction::MSTORE;
}
void CompilerUtils::toSizeAfterFreeMemoryPointer()
{
fetchFreeMemoryPointer();
m_context << eth::Instruction::DUP1 << eth::Instruction::SWAP2 << eth::Instruction::SUB;
m_context << eth::Instruction::SWAP1;
}
unsigned CompilerUtils::loadFromMemory(
unsigned _offset,
@ -187,6 +211,7 @@ unsigned CompilerUtils::getSizeOnStack(vector<shared_ptr<Type const>> const& _va
void CompilerUtils::computeHashStatic(Type const& _type, bool _padToWordBoundaries)
{
unsigned length = storeInMemory(0, _type, _padToWordBoundaries);
solAssert(length <= CompilerUtils::freeMemoryPointer, "");
m_context << u256(length) << u256(0) << eth::Instruction::SHA3;
}

16
libsolidity/CompilerUtils.h

@ -35,6 +35,15 @@ class CompilerUtils
public:
CompilerUtils(CompilerContext& _context): m_context(_context) {}
/// Stores the initial value of the free-memory-pointer at its position;
void initialiseFreeMemoryPointer();
/// Copies the free memory pointer to the stack.
void fetchFreeMemoryPointer();
/// Stores the free memory pointer from the stack.
void storeFreeMemoryPointer();
/// Appends code that transforms memptr to (memptr - free_memptr) memptr
void toSizeAfterFreeMemoryPointer();
/// Loads data from memory to the stack.
/// @param _offset offset in memory (or calldata)
/// @param _type data type to load
@ -67,7 +76,7 @@ public:
bool _padToWordBoundaries = false
);
/// Dynamic version of @see storeInMemory, expects the memory offset below the value on the stack
/// and also updates that.
/// and also updates that. For arrays, only copies the data part.
/// Stack pre: memory_offset value...
/// Stack post: (memory_offset+length)
void storeInMemoryDynamic(Type const& _type, bool _padToWordBoundaries = true);
@ -95,7 +104,10 @@ public:
/// Bytes we need to the start of call data.
/// - The size in bytes of the function (hash) identifier.
static const unsigned int dataStartOffset;
static const unsigned dataStartOffset;
/// Position of the free-memory-pointer in memory;
static const size_t freeMemoryPointer;
private:
/// Prepares the given type for storing in memory by shifting it if necessary.

310
libsolidity/ExpressionCompiler.cpp

@ -73,6 +73,7 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const&
{
if (auto mappingType = dynamic_cast<MappingType const*>(returnType.get()))
{
solAssert(CompilerUtils::freeMemoryPointer >= 0x40, "");
// pop offset
m_context << eth::Instruction::POP;
// move storage offset to memory.
@ -470,21 +471,28 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
_functionCall.getExpression().accept(*this);
solAssert(!function.gasSet(), "Gas limit set for contract creation.");
solAssert(function.getReturnParameterTypes().size() == 1, "");
TypePointers argumentTypes;
for (auto const& arg: arguments)
{
arg->accept(*this);
argumentTypes.push_back(arg->getType());
}
ContractDefinition const& contract = dynamic_cast<ContractType const&>(
*function.getReturnParameterTypes().front()).getContractDefinition();
// copy the contract's code into memory
bytes const& bytecode = m_context.getCompiledContract(contract);
m_context << u256(bytecode.size());
CompilerUtils(m_context).fetchFreeMemoryPointer();
m_context << u256(bytecode.size()) << eth::Instruction::DUP1;
//@todo could be done by actually appending the Assembly, but then we probably need to compile
// multiple times. Will revisit once external fuctions are inlined.
m_context.appendData(bytecode);
//@todo copy to memory position 0, shift as soon as we use memory
m_context << u256(0) << eth::Instruction::CODECOPY;
m_context << eth::Instruction::DUP4 << eth::Instruction::CODECOPY;
m_context << u256(bytecode.size());
appendArgumentsCopyToMemory(arguments, function.getParameterTypes());
// size, offset, endowment
m_context << u256(0);
m_context << eth::Instruction::ADD;
encodeToMemory(argumentTypes, function.getParameterTypes());
// now on stack: memory_end_ptr
// need: size, offset, endowment
CompilerUtils(m_context).toSizeAfterFreeMemoryPointer();
if (function.valueSet())
m_context << eth::dupInstruction(3);
else
@ -546,12 +554,16 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
break;
case Location::SHA3:
{
// we might compute a sha as part of argumentsAppendCopyToMemory, this is only a hack
// and should be removed once we have a real free memory pointer
m_context << u256(0x40);
appendArgumentsCopyToMemory(arguments, TypePointers(), function.padArguments(), false, true);
m_context << u256(0x40) << eth::Instruction::SWAP1 << eth::Instruction::SUB;
m_context << u256(0x40) << eth::Instruction::SHA3;
TypePointers argumentTypes;
for (auto const& arg: arguments)
{
arg->accept(*this);
argumentTypes.push_back(arg->getType());
}
CompilerUtils(m_context).fetchFreeMemoryPointer();
encodeToMemory(argumentTypes, TypePointers(), function.padArguments(), true);
CompilerUtils(m_context).toSizeAfterFreeMemoryPointer();
m_context << eth::Instruction::SHA3;
break;
}
case Location::Log0:
@ -566,9 +578,15 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
arguments[arg]->accept(*this);
appendTypeConversion(*arguments[arg]->getType(), *function.getParameterTypes()[arg], true);
}
m_context << u256(0);
appendExpressionCopyToMemory(*function.getParameterTypes().front(), *arguments.front());
m_context << u256(0) << eth::logInstruction(logNumber);
arguments.front()->accept(*this);
CompilerUtils(m_context).fetchFreeMemoryPointer();
encodeToMemory(
{arguments.front()->getType()},
{function.getParameterTypes().front()},
false,
true);
CompilerUtils(m_context).toSizeAfterFreeMemoryPointer();
m_context << eth::logInstruction(logNumber);
break;
}
case Location::Event:
@ -582,8 +600,11 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
{
++numIndexed;
arguments[arg - 1]->accept(*this);
appendTypeConversion(*arguments[arg - 1]->getType(),
*function.getParameterTypes()[arg - 1], true);
appendTypeConversion(
*arguments[arg - 1]->getType(),
*function.getParameterTypes()[arg - 1],
true
);
}
if (!event.isAnonymous())
{
@ -593,18 +614,20 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
solAssert(numIndexed <= 4, "Too many indexed arguments.");
// Copy all non-indexed arguments to memory (data)
// Memory position is only a hack and should be removed once we have free memory pointer.
m_context << u256(0x40);
vector<ASTPointer<Expression const>> nonIndexedArgs;
TypePointers nonIndexedTypes;
TypePointers nonIndexedArgTypes;
TypePointers nonIndexedParamTypes;
for (unsigned arg = 0; arg < arguments.size(); ++arg)
if (!event.getParameters()[arg]->isIndexed())
{
nonIndexedArgs.push_back(arguments[arg]);
nonIndexedTypes.push_back(function.getParameterTypes()[arg]);
arguments[arg]->accept(*this);
nonIndexedArgTypes.push_back(arguments[arg]->getType());
nonIndexedParamTypes.push_back(function.getParameterTypes()[arg]);
}
appendArgumentsCopyToMemory(nonIndexedArgs, nonIndexedTypes);
m_context << u256(0x40) << eth::Instruction::SWAP1 << eth::Instruction::SUB;
m_context << u256(0x40) << eth::logInstruction(numIndexed);
CompilerUtils(m_context).fetchFreeMemoryPointer();
encodeToMemory(nonIndexedArgTypes, nonIndexedParamTypes);
// need: topic1 ... topicn memsize memstart
CompilerUtils(m_context).toSizeAfterFreeMemoryPointer();
m_context << eth::logInstruction(numIndexed);
break;
}
case Location::BlockHash:
@ -804,8 +827,10 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
Type const& keyType = *dynamic_cast<MappingType const&>(baseType).getKeyType();
m_context << u256(0); // memory position
solAssert(_indexAccess.getIndexExpression(), "Index expression expected.");
solAssert(keyType.getCalldataEncodedSize() <= 0x20, "Dynamic keys not yet implemented.");
appendExpressionCopyToMemory(keyType, *_indexAccess.getIndexExpression());
m_context << eth::Instruction::SWAP1;
solAssert(CompilerUtils::freeMemoryPointer >= 0x40, "");
appendTypeMoveToMemory(IntegerType(256));
m_context << u256(0) << eth::Instruction::SHA3;
m_context << u256(0);
@ -1058,42 +1083,81 @@ void ExpressionCompiler::appendExternalFunctionCall(
unsigned gasStackPos = m_context.currentToBaseStackOffset(gasValueSize);
unsigned valueStackPos = m_context.currentToBaseStackOffset(1);
bool returnSuccessCondition =
_functionType.getLocation() == FunctionType::Location::Bare ||
_functionType.getLocation() == FunctionType::Location::BareCallCode;
using FunctionKind = FunctionType::Location;
FunctionKind funKind = _functionType.getLocation();
bool returnSuccessCondition = funKind == FunctionKind::Bare || funKind == FunctionKind::BareCallCode;
//@todo only return the first return value for now
Type const* firstType = _functionType.getReturnParameterTypes().empty() ? nullptr :
_functionType.getReturnParameterTypes().front().get();
unsigned retSize = firstType ? firstType->getCalldataEncodedSize() : 0;
Type const* firstReturnType =
_functionType.getReturnParameterTypes().empty() ?
nullptr :
_functionType.getReturnParameterTypes().front().get();
unsigned retSize = firstReturnType ? firstReturnType->getCalldataEncodedSize() : 0;
if (returnSuccessCondition)
retSize = 0; // return value actually is success condition
m_context << u256(retSize) << u256(0);
if (_functionType.isBareCall())
m_context << u256(0);
else
// Evaluate arguments.
TypePointers argumentTypes;
bool manualFunctionId =
(funKind == FunctionKind::Bare || funKind == FunctionKind::BareCallCode) &&
!_arguments.empty() &&
_arguments.front()->getType()->getRealType()->getCalldataEncodedSize(false) ==
CompilerUtils::dataStartOffset;
if (manualFunctionId)
{
// copy function identifier
m_context << eth::dupInstruction(gasValueSize + 3);
CompilerUtils(m_context).storeInMemory(0, IntegerType(CompilerUtils::dataStartOffset * 8));
m_context << u256(CompilerUtils::dataStartOffset);
// If we have a BareCall or BareCallCode and the first type has exactly 4 bytes, use it as
// function identifier.
_arguments.front()->accept(*this);
appendTypeConversion(
*_arguments.front()->getType(),
IntegerType(8 * CompilerUtils::dataStartOffset),
true
);
for (unsigned i = 0; i < gasValueSize; ++i)
m_context << eth::swapInstruction(gasValueSize - i);
gasStackPos++;
valueStackPos++;
}
for (size_t i = manualFunctionId ? 1 : 0; i < _arguments.size(); ++i)
{
_arguments[i]->accept(*this);
argumentTypes.push_back(_arguments[i]->getType());
}
// For bare call, activate "4 byte pad exception": If the first argument has exactly 4 bytes,
// do not pad it to 32 bytes.
// Copy function identifier to memory.
CompilerUtils(m_context).fetchFreeMemoryPointer();
if (!_functionType.isBareCall() || manualFunctionId)
{
m_context << eth::dupInstruction(2 + gasValueSize + CompilerUtils::getSizeOnStack(argumentTypes));
appendTypeMoveToMemory(IntegerType(8 * CompilerUtils::dataStartOffset), false);
}
// If the function takes arbitrary parameters, copy dynamic length data in place.
appendArgumentsCopyToMemory(
_arguments,
// Move argumenst to memory, will not update the free memory pointer (but will update the memory
// pointer on the stack).
encodeToMemory(
argumentTypes,
_functionType.getParameterTypes(),
_functionType.padArguments(),
_functionType.getLocation() == FunctionType::Location::Bare ||
_functionType.getLocation() == FunctionType::Location::BareCallCode,
_functionType.takesArbitraryParameters()
);
// CALL arguments: outSize, outOff, inSize, (already present up to here)
// inOff, value, addr, gas (stack top)
m_context << u256(0);
// Stack now:
// <stack top>
// input_memory_end
// value [if _functionType.valueSet()]
// gas [if _functionType.gasSet()]
// function identifier [unless bare]
// contract address
// Output data will replace input data.
// put on stack: <size of output> <memory pos of output> <size of input> <memory pos of input>
m_context << u256(retSize);
CompilerUtils(m_context).fetchFreeMemoryPointer();
m_context << eth::Instruction::DUP1 << eth::Instruction::DUP4 << eth::Instruction::SUB;
m_context << eth::Instruction::DUP2;
// CALL arguments: outSize, outOff, inSize, inOff (already present up to here)
// value, addr, gas (stack top)
if (_functionType.valueSet())
m_context << eth::dupInstruction(m_context.baseToCurrentStackOffset(valueStackPos));
else
@ -1109,19 +1173,16 @@ void ExpressionCompiler::appendExternalFunctionCall(
u256(eth::c_callGas + 10 + (_functionType.valueSet() ? eth::c_callValueTransferGas : 0) + eth::c_callNewAccountGas) <<
eth::Instruction::GAS <<
eth::Instruction::SUB;
if (
_functionType.getLocation() == FunctionType::Location::CallCode ||
_functionType.getLocation() == FunctionType::Location::BareCallCode
)
if (funKind == FunctionKind::CallCode || funKind == FunctionKind::BareCallCode)
m_context << eth::Instruction::CALLCODE;
else
m_context << eth::Instruction::CALL;
unsigned remainsSize =
1 + // contract address
2 + // contract address, input_memory_end
_functionType.valueSet() +
_functionType.gasSet() +
!_functionType.isBareCall();
(!_functionType.isBareCall() || manualFunctionId);
if (returnSuccessCondition)
m_context << eth::swapInstruction(remainsSize);
@ -1138,52 +1199,93 @@ void ExpressionCompiler::appendExternalFunctionCall(
{
// already there
}
else if (_functionType.getLocation() == FunctionType::Location::RIPEMD160)
else if (funKind == FunctionKind::RIPEMD160)
{
// fix: built-in contract returns right-aligned data
CompilerUtils(m_context).loadFromMemory(0, IntegerType(160), false, true);
CompilerUtils(m_context).fetchFreeMemoryPointer();
CompilerUtils(m_context).loadFromMemoryDynamic(IntegerType(160), false, true, false);
appendTypeConversion(IntegerType(160), FixedBytesType(20));
}
else if (firstType)
CompilerUtils(m_context).loadFromMemory(0, *firstType, false, true);
else if (firstReturnType)
{
//@todo manually update free memory pointer if we accept returning memory-stored objects
CompilerUtils(m_context).fetchFreeMemoryPointer();
CompilerUtils(m_context).loadFromMemoryDynamic(*firstReturnType, false, true, false);
}
}
void ExpressionCompiler::appendArgumentsCopyToMemory(
vector<ASTPointer<Expression const>> const& _arguments,
TypePointers const& _types,
void ExpressionCompiler::encodeToMemory(
TypePointers const& _givenTypes,
TypePointers const& _targetTypes,
bool _padToWordBoundaries,
bool _padExceptionIfFourBytes,
bool _copyDynamicDataInPlace
)
{
solAssert(_types.empty() || _types.size() == _arguments.size(), "");
TypePointers types = _types;
if (_types.empty())
for (ASTPointer<Expression const> const& argument: _arguments)
types.push_back(argument->getType()->getRealType());
vector<size_t> dynamicArguments;
unsigned stackSizeOfDynamicTypes = 0;
for (size_t i = 0; i < _arguments.size(); ++i)
// stack: <v1> <v2> ... <vn> <mem>
TypePointers targetTypes = _targetTypes.empty() ? _givenTypes : _targetTypes;
solAssert(targetTypes.size() == _givenTypes.size(), "");
for (TypePointer& t: targetTypes)
t = t->getRealType()->externalType();
// Stack during operation:
// <v1> <v2> ... <vn> <mem_start> <dyn_head_1> ... <dyn_head_r> <end_of_mem>
// The values dyn_head_i are added during the first loop and they point to the head part
// of the ith dynamic parameter, which is filled once the dynamic parts are processed.
// store memory start pointer
m_context << eth::Instruction::DUP1;
unsigned argSize = CompilerUtils::getSizeOnStack(_givenTypes);
unsigned stackPos = 0; // advances through the argument values
unsigned dynPointers = 0; // number of dynamic head pointers on the stack
for (size_t i = 0; i < _givenTypes.size(); ++i)
{
_arguments[i]->accept(*this);
TypePointer argType = types[i]->externalType();
solAssert(!!argType, "Externalable type expected.");
if (argType->isValueType())
appendTypeConversion(*_arguments[i]->getType(), *argType, true);
TypePointer targetType = targetTypes[i];
solAssert(!!targetType, "Externalable type expected.");
if (targetType->isDynamicallySized() && !_copyDynamicDataInPlace)
{
// leave end_of_mem as dyn head pointer
m_context << eth::Instruction::DUP1 << u256(32) << eth::Instruction::ADD;
dynPointers++;
}
else
argType = _arguments[i]->getType()->getRealType()->externalType();
solAssert(!!argType, "Externalable type expected.");
bool pad = _padToWordBoundaries;
// Do not pad if the first argument has exactly four bytes
if (i == 0 && pad && _padExceptionIfFourBytes && argType->getCalldataEncodedSize(false) == 4)
pad = false;
if (!_copyDynamicDataInPlace && argType->isDynamicallySized())
{
solAssert(argType->getCategory() == Type::Category::Array, "Unknown dynamic type.");
auto const& arrayType = dynamic_cast<ArrayType const&>(*_arguments[i]->getType());
// move memory reference to top of stack
CompilerUtils(m_context).moveToStackTop(arrayType.getSizeOnStack());
CompilerUtils(m_context).copyToStackTop(
argSize - stackPos + dynPointers + 2,
_givenTypes[i]->getSizeOnStack()
);
if (targetType->isValueType())
appendTypeConversion(*_givenTypes[i], *targetType, true);
solAssert(!!targetType, "Externalable type expected.");
appendTypeMoveToMemory(*targetType, _padToWordBoundaries);
}
stackPos += _givenTypes[i]->getSizeOnStack();
}
// now copy the dynamic part
// Stack: <v1> <v2> ... <vn> <mem_start> <dyn_head_1> ... <dyn_head_r> <end_of_mem>
stackPos = 0;
unsigned thisDynPointer = 0;
for (size_t i = 0; i < _givenTypes.size(); ++i)
{
TypePointer targetType = targetTypes[i];
solAssert(!!targetType, "Externalable type expected.");
if (targetType->isDynamicallySized() && !_copyDynamicDataInPlace)
{
solAssert(_givenTypes[i]->getCategory() == Type::Category::Array, "Unknown dynamic type.");
auto const& arrayType = dynamic_cast<ArrayType const&>(*_givenTypes[i]);
// copy tail pointer (=mem_end - mem_start) to memory
m_context << eth::dupInstruction(2 + dynPointers) << eth::Instruction::DUP2;
m_context << eth::Instruction::SUB;
m_context << eth::dupInstruction(2 + dynPointers - thisDynPointer);
m_context << eth::Instruction::MSTORE;
// now copy the array
CompilerUtils(m_context).copyToStackTop(
argSize - stackPos + dynPointers + 2,
arrayType.getSizeOnStack()
);
// copy length to memory
m_context << eth::dupInstruction(1 + arrayType.getSizeOnStack());
if (arrayType.location() == ReferenceType::Location::CallData)
m_context << eth::Instruction::DUP2; // length is on stack
else if (arrayType.location() == ReferenceType::Location::Storage)
@ -1194,31 +1296,19 @@ void ExpressionCompiler::appendArgumentsCopyToMemory(
m_context << eth::Instruction::DUP2 << eth::Instruction::MLOAD;
}
appendTypeMoveToMemory(IntegerType(256), true);
stackSizeOfDynamicTypes += arrayType.getSizeOnStack();
dynamicArguments.push_back(i);
}
else
appendTypeMoveToMemory(*argType, pad);
}
// copy the new memory pointer
m_context << eth::swapInstruction(arrayType.getSizeOnStack() + 1) << eth::Instruction::POP;
// copy data part
appendTypeMoveToMemory(arrayType, true);
// copy dynamic values to memory
unsigned dynStackPointer = stackSizeOfDynamicTypes;
// stack layout: <dyn arg 1> ... <dyn arg m> <memory pointer>
for (size_t i: dynamicArguments)
{
auto const& arrayType = dynamic_cast<ArrayType const&>(*_arguments[i]->getType());
CompilerUtils(m_context).copyToStackTop(1 + dynStackPointer, arrayType.getSizeOnStack());
dynStackPointer -= arrayType.getSizeOnStack();
appendTypeMoveToMemory(arrayType, true);
thisDynPointer++;
}
stackPos += _givenTypes[i]->getSizeOnStack();
}
solAssert(dynStackPointer == 0, "");
// remove dynamic values (and retain memory pointer)
if (stackSizeOfDynamicTypes > 0)
{
m_context << eth::swapInstruction(stackSizeOfDynamicTypes);
CompilerUtils(m_context).popStackSlots(stackSizeOfDynamicTypes);
}
// remove unneeded stack elements (and retain memory pointer)
m_context << eth::swapInstruction(argSize + dynPointers + 1);
CompilerUtils(m_context).popStackSlots(argSize + dynPointers + 1);
}
void ExpressionCompiler::appendTypeMoveToMemory(Type const& _type, bool _padToWordBoundaries)

25
libsolidity/ExpressionCompiler.h

@ -98,21 +98,28 @@ private:
void appendHighBitsCleanup(IntegerType const& _typeOnStack);
/// Appends code to call a function of the given type with the given arguments.
void appendExternalFunctionCall(FunctionType const& _functionType, std::vector<ASTPointer<Expression const>> const& _arguments);
/// Appends code that evaluates the given arguments and moves the result to memory encoded as
/// specified by the ABI. The memory offset is expected to be on the stack and is updated by
/// this call. If @a _padToWordBoundaries is set to false, all values are concatenated without
/// padding. If @a _copyDynamicDataInPlace is set, dynamic types is stored (without length)
void appendExternalFunctionCall(
FunctionType const& _functionType,
std::vector<ASTPointer<Expression const>> const& _arguments
);
/// Copies values (of types @a _givenTypes) given on the stack to a location in memory given
/// at the stack top, encoding them according to the ABI as the given types @a _targetTypes.
/// Removes the values from the stack and leaves the updated memory pointer.
/// Stack pre: <v1> <v2> ... <vn> <memptr>
/// Stack post: <memptr_updated>
/// Does not touch the memory-free pointer.
/// @param _padToWordBoundaries if false, all values are concatenated without padding.
/// @param _copyDynamicDataInPlace if true, dynamic types is stored (without length)
/// together with fixed-length data.
void appendArgumentsCopyToMemory(
std::vector<ASTPointer<Expression const>> const& _arguments,
TypePointers const& _types = {},
void encodeToMemory(
TypePointers const& _givenTypes = {},
TypePointers const& _targetTypes = {},
bool _padToWordBoundaries = true,
bool _padExceptionIfFourBytes = false,
bool _copyDynamicDataInPlace = false
);
/// Appends code that moves a stack element of the given type to memory. The memory offset is
/// expected below the stack element and is updated by this call.
/// For arrays, this only copies the data part.
void appendTypeMoveToMemory(Type const& _type, bool _padToWordBoundaries = true);
/// Appends code that evaluates a single expression and moves the result to memory. The memory offset is
/// expected to be on the stack and is updated by this call.

111
libsolidity/InterfaceHandler.cpp

@ -16,8 +16,10 @@ InterfaceHandler::InterfaceHandler()
m_lastTag = DocTagType::None;
}
std::unique_ptr<std::string> InterfaceHandler::getDocumentation(ContractDefinition const& _contractDef,
DocumentationType _type)
unique_ptr<string> InterfaceHandler::getDocumentation(
ContractDefinition const& _contractDef,
DocumentationType _type
)
{
switch(_type)
{
@ -35,7 +37,7 @@ std::unique_ptr<std::string> InterfaceHandler::getDocumentation(ContractDefiniti
return nullptr;
}
std::unique_ptr<std::string> InterfaceHandler::getABIInterface(ContractDefinition const& _contractDef)
unique_ptr<string> InterfaceHandler::getABIInterface(ContractDefinition const& _contractDef)
{
Json::Value abi(Json::arrayValue);
@ -101,7 +103,7 @@ std::unique_ptr<std::string> InterfaceHandler::getABIInterface(ContractDefinitio
event["inputs"] = params;
abi.append(event);
}
return std::unique_ptr<std::string>(new std::string(Json::FastWriter().write(abi)));
return unique_ptr<string>(new string(Json::FastWriter().write(abi)));
}
unique_ptr<string> InterfaceHandler::getABISolidityInterface(ContractDefinition const& _contractDef)
@ -141,7 +143,7 @@ unique_ptr<string> InterfaceHandler::getABISolidityInterface(ContractDefinition
return unique_ptr<string>(new string(ret + "}"));
}
std::unique_ptr<std::string> InterfaceHandler::getUserDocumentation(ContractDefinition const& _contractDef)
unique_ptr<string> InterfaceHandler::getUserDocumentation(ContractDefinition const& _contractDef)
{
Json::Value doc;
Json::Value methods(Json::objectValue);
@ -163,10 +165,10 @@ std::unique_ptr<std::string> InterfaceHandler::getUserDocumentation(ContractDefi
}
doc["methods"] = methods;
return std::unique_ptr<std::string>(new std::string(Json::FastWriter().write(doc)));
return unique_ptr<string>(new string(Json::FastWriter().write(doc)));
}
std::unique_ptr<std::string> InterfaceHandler::getDevDocumentation(ContractDefinition const& _contractDef)
unique_ptr<string> InterfaceHandler::getDevDocumentation(ContractDefinition const& _contractDef)
{
// LTODO: Somewhere in this function warnings for mismatch of param names
// should be thrown
@ -203,7 +205,7 @@ std::unique_ptr<std::string> InterfaceHandler::getDevDocumentation(ContractDefin
method["author"] = m_author;
Json::Value params(Json::objectValue);
std::vector<std::string> paramNames = it.second->getParameterNames();
vector<string> paramNames = it.second->getParameterNames();
for (auto const& pair: m_params)
{
if (find(paramNames.begin(), paramNames.end(), pair.first) == paramNames.end())
@ -227,7 +229,7 @@ std::unique_ptr<std::string> InterfaceHandler::getDevDocumentation(ContractDefin
}
doc["methods"] = methods;
return std::unique_ptr<std::string>(new std::string(Json::FastWriter().write(doc)));
return unique_ptr<string>(new string(Json::FastWriter().write(doc)));
}
/* -- private -- */
@ -244,48 +246,56 @@ void InterfaceHandler::resetDev()
m_params.clear();
}
static inline std::string::const_iterator skipLineOrEOS(std::string::const_iterator _nlPos,
std::string::const_iterator _end)
static inline string::const_iterator skipLineOrEOS(
string::const_iterator _nlPos,
string::const_iterator _end
)
{
return (_nlPos == _end) ? _end : ++_nlPos;
}
std::string::const_iterator InterfaceHandler::parseDocTagLine(std::string::const_iterator _pos,
std::string::const_iterator _end,
std::string& _tagString,
DocTagType _tagType,
bool _appending)
string::const_iterator InterfaceHandler::parseDocTagLine(
string::const_iterator _pos,
string::const_iterator _end,
string& _tagString,
DocTagType _tagType,
bool _appending
)
{
auto nlPos = std::find(_pos, _end, '\n');
auto nlPos = find(_pos, _end, '\n');
if (_appending && _pos < _end && *_pos != ' ')
_tagString += " ";
std::copy(_pos, nlPos, back_inserter(_tagString));
copy(_pos, nlPos, back_inserter(_tagString));
m_lastTag = _tagType;
return skipLineOrEOS(nlPos, _end);
}
std::string::const_iterator InterfaceHandler::parseDocTagParam(std::string::const_iterator _pos,
std::string::const_iterator _end)
string::const_iterator InterfaceHandler::parseDocTagParam(
string::const_iterator _pos,
string::const_iterator _end
)
{
// find param name
auto currPos = std::find(_pos, _end, ' ');
auto currPos = find(_pos, _end, ' ');
if (currPos == _end)
BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("End of param name not found" + std::string(_pos, _end)));
BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("End of param name not found" + string(_pos, _end)));
auto paramName = std::string(_pos, currPos);
auto paramName = string(_pos, currPos);
currPos += 1;
auto nlPos = std::find(currPos, _end, '\n');
auto paramDesc = std::string(currPos, nlPos);
m_params.push_back(std::make_pair(paramName, paramDesc));
auto nlPos = find(currPos, _end, '\n');
auto paramDesc = string(currPos, nlPos);
m_params.push_back(make_pair(paramName, paramDesc));
m_lastTag = DocTagType::Param;
return skipLineOrEOS(nlPos, _end);
}
std::string::const_iterator InterfaceHandler::appendDocTagParam(std::string::const_iterator _pos,
std::string::const_iterator _end)
string::const_iterator InterfaceHandler::appendDocTagParam(
string::const_iterator _pos,
string::const_iterator _end
)
{
// Should never be called with an empty vector
solAssert(!m_params.empty(), "Internal: Tried to append to empty parameter");
@ -293,18 +303,20 @@ std::string::const_iterator InterfaceHandler::appendDocTagParam(std::string::con
auto pair = m_params.back();
if (_pos < _end && *_pos != ' ')
pair.second += " ";
auto nlPos = std::find(_pos, _end, '\n');
std::copy(_pos, nlPos, back_inserter(pair.second));
auto nlPos = find(_pos, _end, '\n');
copy(_pos, nlPos, back_inserter(pair.second));
m_params.at(m_params.size() - 1) = pair;
return skipLineOrEOS(nlPos, _end);
}
std::string::const_iterator InterfaceHandler::parseDocTag(std::string::const_iterator _pos,
std::string::const_iterator _end,
std::string const& _tag,
CommentOwner _owner)
string::const_iterator InterfaceHandler::parseDocTag(
string::const_iterator _pos,
string::const_iterator _end,
string const& _tag,
CommentOwner _owner
)
{
// LTODO: need to check for @(start of a tag) between here and the end of line
// for all cases. Also somehow automate list of acceptable tags for each
@ -345,9 +357,11 @@ std::string::const_iterator InterfaceHandler::parseDocTag(std::string::const_ite
return appendDocTag(_pos, _end, _owner);
}
std::string::const_iterator InterfaceHandler::appendDocTag(std::string::const_iterator _pos,
std::string::const_iterator _end,
CommentOwner _owner)
string::const_iterator InterfaceHandler::appendDocTag(
string::const_iterator _pos,
string::const_iterator _end,
CommentOwner _owner
)
{
switch (m_lastTag)
{
@ -379,33 +393,36 @@ std::string::const_iterator InterfaceHandler::appendDocTag(std::string::const_it
}
}
static inline std::string::const_iterator getFirstSpaceOrNl(std::string::const_iterator _pos,
std::string::const_iterator _end)
static inline string::const_iterator getFirstSpaceOrNl(
string::const_iterator _pos,
string::const_iterator _end
)
{
auto spacePos = std::find(_pos, _end, ' ');
auto nlPos = std::find(_pos, _end, '\n');
auto spacePos = find(_pos, _end, ' ');
auto nlPos = find(_pos, _end, '\n');
return (spacePos < nlPos) ? spacePos : nlPos;
}
void InterfaceHandler::parseDocString(std::string const& _string, CommentOwner _owner)
void InterfaceHandler::parseDocString(string const& _string, CommentOwner _owner)
{
auto currPos = _string.begin();
auto end = _string.end();
while (currPos != end)
{
auto tagPos = std::find(currPos, end, '@');
auto nlPos = std::find(currPos, end, '\n');
auto tagPos = find(currPos, end, '@');
auto nlPos = find(currPos, end, '\n');
if (tagPos != end && tagPos < nlPos)
{
// we found a tag
auto tagNameEndPos = getFirstSpaceOrNl(tagPos, end);
if (tagNameEndPos == end)
BOOST_THROW_EXCEPTION(DocstringParsingError() <<
errinfo_comment("End of tag " + std::string(tagPos, tagNameEndPos) + "not found"));
BOOST_THROW_EXCEPTION(
DocstringParsingError() <<
errinfo_comment("End of tag " + string(tagPos, tagNameEndPos) + "not found"));
currPos = parseDocTag(tagNameEndPos + 1, end, std::string(tagPos + 1, tagNameEndPos), _owner);
currPos = parseDocTag(tagNameEndPos + 1, end, string(tagPos + 1, tagNameEndPos), _owner);
}
else if (m_lastTag != DocTagType::None) // continuation of the previous tag
currPos = appendDocTag(currPos, end, _owner);

48
libsolidity/InterfaceHandler.h

@ -67,8 +67,10 @@ public:
/// types provided by @c DocumentationType
/// @return A unique pointer contained string with the json
/// representation of provided type
std::unique_ptr<std::string> getDocumentation(ContractDefinition const& _contractDef,
DocumentationType _type);
std::unique_ptr<std::string> getDocumentation(
ContractDefinition const& _contractDef,
DocumentationType _type
);
/// Get the ABI Interface of the contract
/// @param _contractDef The contract definition
/// @return A unique pointer contained string with the json
@ -90,23 +92,33 @@ private:
void resetUser();
void resetDev();
std::string::const_iterator parseDocTagLine(std::string::const_iterator _pos,
std::string::const_iterator _end,
std::string& _tagString,
DocTagType _tagType,
bool _appending);
std::string::const_iterator parseDocTagParam(std::string::const_iterator _pos,
std::string::const_iterator _end);
std::string::const_iterator appendDocTagParam(std::string::const_iterator _pos,
std::string::const_iterator _end);
std::string::const_iterator parseDocTagLine(
std::string::const_iterator _pos,
std::string::const_iterator _end,
std::string& _tagString,
DocTagType _tagType,
bool _appending
);
std::string::const_iterator parseDocTagParam(
std::string::const_iterator _pos,
std::string::const_iterator _end
);
std::string::const_iterator appendDocTagParam(
std::string::const_iterator _pos,
std::string::const_iterator _end
);
void parseDocString(std::string const& _string, CommentOwner _owner);
std::string::const_iterator appendDocTag(std::string::const_iterator _pos,
std::string::const_iterator _end,
CommentOwner _owner);
std::string::const_iterator parseDocTag(std::string::const_iterator _pos,
std::string::const_iterator _end,
std::string const& _tag,
CommentOwner _owner);
std::string::const_iterator appendDocTag(
std::string::const_iterator _pos,
std::string::const_iterator _end,
CommentOwner _owner
);
std::string::const_iterator parseDocTag(
std::string::const_iterator _pos,
std::string::const_iterator _end,
std::string const& _tag,
CommentOwner _owner
);
// internal state
DocTagType m_lastTag;

3
libsolidity/Token.h

@ -253,7 +253,6 @@ namespace solidity
K(UInt240, "uint240", 0) \
K(UInt248, "uint248", 0) \
K(UInt256, "uint256", 0) \
K(Bytes0, "bytes0", 0) \
K(Bytes1, "bytes1", 0) \
K(Bytes2, "bytes2", 0) \
K(Bytes3, "bytes3", 0) \
@ -306,7 +305,7 @@ namespace solidity
/* Identifiers (not keywords or future reserved words). */ \
T(Identifier, NULL, 0) \
\
/* Keywords reserved for future. use*/ \
/* Keywords reserved for future. use. */ \
K(As, "as", 0) \
K(Case, "case", 0) \
K(Catch, "catch", 0) \

4
libsolidity/Types.cpp

@ -121,7 +121,7 @@ TypePointer Type::fromElementaryTypeName(Token::Value _typeToken)
{
int offset = _typeToken - Token::Int;
int bytes = offset % 33;
if (bytes == 0 && _typeToken != Token::Bytes0)
if (bytes == 0 && _typeToken != Token::Bytes1)
bytes = 32;
int modifier = offset / 33;
switch(modifier)
@ -131,7 +131,7 @@ TypePointer Type::fromElementaryTypeName(Token::Value _typeToken)
case 1:
return make_shared<IntegerType>(bytes * 8, IntegerType::Modifier::Unsigned);
case 2:
return make_shared<FixedBytesType>(bytes);
return make_shared<FixedBytesType>(bytes + 1);
default:
solAssert(false, "Unexpected modifier value. Should never happen");
return TypePointer();

149
libweb3jsonrpc/WebThreeStubServerBase.cpp

@ -164,15 +164,33 @@ static Json::Value toJson(dev::eth::Transaction const& _t)
static Json::Value toJson(dev::eth::LocalisedLogEntry const& _e)
{
Json::Value res;
if (_e.transactionHash)
if (_e.topics.size() > 0)
{
res["data"] = toJS(_e.data);
res["address"] = toJS(_e.address);
res["topics"] = Json::Value(Json::arrayValue);
for (auto const& t: _e.topics)
res["topics"].append(toJS(t));
res["number"] = _e.number;
res["hash"] = toJS(_e.transactionHash);
if (_e.mined)
{
res["type"] = "mined";
res["blockNumber"] = _e.blockNumber;
res["blockHash"] = toJS(_e.blockHash);
res["logIndex"] = _e.logIndex;
res["transactionHash"] = toJS(_e.transactionHash);
res["transactionIndex"] = _e.transactionIndex;
}
else
{
res["type"] = "pending";
res["blockNumber"] = Json::Value(Json::nullValue);
res["blockHash"] = Json::Value(Json::nullValue);
res["logIndex"] = Json::Value(Json::nullValue);
res["transactionHash"] = Json::Value(Json::nullValue);
res["transactionIndex"] = Json::Value(Json::nullValue);
}
} else {
res = toJS(_e.special);
}
return res;
}
@ -193,7 +211,42 @@ static Json::Value toJson(map<u256, u256> const& _storage)
return res;
}
static dev::eth::LogFilter toLogFilter(Json::Value const& _json) // commented to avoid warning. Uncomment once in use @ PoC-7.
static dev::eth::LogFilter toLogFilter(Json::Value const& _json)
{
dev::eth::LogFilter filter;
if (!_json.isObject() || _json.empty())
return filter;
// check only !empty. it should throw exceptions if input params are incorrect
if (!_json["fromBlock"].empty())
filter.withEarliest(jsToFixed<32>(_json["fromBlock"].asString()));
if (!_json["toBlock"].empty())
filter.withLatest(jsToFixed<32>(_json["toBlock"].asString()));
if (!_json["address"].empty())
{
if (_json["address"].isArray())
for (auto i : _json["address"])
filter.address(jsToAddress(i.asString()));
else
filter.address(jsToAddress(_json["address"].asString()));
}
if (!_json["topics"].empty())
for (unsigned i = 0; i < _json["topics"].size(); i++)
{
if (_json["topics"][i].isArray())
{
for (auto t: _json["topics"][i])
if (!t.isNull())
filter.topic(i, jsToFixed<32>(t.asString()));
}
else if (!_json["topics"][i].isNull()) // if it is anything else then string, it should and will fail
filter.topic(i, jsToFixed<32>(_json["topics"][i].asString()));
}
return filter;
}
// TODO: this should be removed once we decide to remove backward compatibility with old log filters
static dev::eth::LogFilter toLogFilter(Json::Value const& _json, Interface const& _client) // commented to avoid warning. Uncomment once in use @ PoC-7.
{
dev::eth::LogFilter filter;
if (!_json.isObject() || _json.empty())
@ -201,9 +254,9 @@ static dev::eth::LogFilter toLogFilter(Json::Value const& _json) // commented to
// check only !empty. it should throw exceptions if input params are incorrect
if (!_json["fromBlock"].empty())
filter.withEarliest(jsToBlockNumber(_json["fromBlock"].asString()));
filter.withEarliest(_client.hashFromNumber(jsToBlockNumber(_json["fromBlock"].asString())));
if (!_json["toBlock"].empty())
filter.withLatest(jsToBlockNumber(_json["toBlock"].asString()));
filter.withLatest(_client.hashFromNumber(jsToBlockNumber(_json["toBlock"].asString())));
if (!_json["address"].empty())
{
if (_json["address"].isArray())
@ -712,25 +765,24 @@ Json::Value WebThreeStubServerBase::eth_getCompilers()
}
string WebThreeStubServerBase::eth_compileLLL(string const& _code)
string WebThreeStubServerBase::eth_compileLLL(string const& _source)
{
// TODO throw here jsonrpc errors
string res;
vector<string> errors;
res = toJS(dev::eth::compileLLL(_code, true, &errors));
res = toJS(dev::eth::compileLLL(_source, true, &errors));
cwarn << "LLL compilation errors: " << errors;
return res;
}
string WebThreeStubServerBase::eth_compileSerpent(string const& _code)
string WebThreeStubServerBase::eth_compileSerpent(string const& _source)
{
// TODO throw here jsonrpc errors
string res;
(void)_code;
#if ETH_SERPENT || !ETH_TRUE
try
{
res = toJS(dev::asBytes(::compile(_code)));
res = toJS(dev::asBytes(::compile(_source)));
}
catch (string err)
{
@ -740,36 +792,74 @@ string WebThreeStubServerBase::eth_compileSerpent(string const& _code)
{
cwarn << "Uncought serpent compilation exception";
}
#else
(void)_source;
#endif
return res;
}
string WebThreeStubServerBase::eth_compileSolidity(string const& _code)
Json::Value WebThreeStubServerBase::eth_compileSolidity(string const& _source)
{
// TOOD throw here jsonrpc errors
(void)_code;
string res;
Json::Value res(Json::objectValue);
#if ETH_SOLIDITY || !ETH_TRUE
dev::solidity::CompilerStack compiler;
try
{
res = toJS(compiler.compile(_code, true));
compiler.addSource("source", _source);
compiler.compile();
for (string const& name: compiler.getContractNames())
{
Json::Value contract(Json::objectValue);
contract["code"] = toJS(compiler.getBytecode(name));
Json::Value info(Json::objectValue);
info["source"] = _source;
info["language"] = "";
info["languageVersion"] = "";
info["compilerVersion"] = "";
Json::Reader reader;
reader.parse(compiler.getInterface(name), info["abiDefinition"]);
reader.parse(compiler.getMetadata(name, dev::solidity::DocumentationType::NatspecUser), info["userDoc"]);
reader.parse(compiler.getMetadata(name, dev::solidity::DocumentationType::NatspecDev), info["developerDoc"]);
contract["info"] = info;
res[name] = contract;
}
}
catch (dev::Exception const& exception)
{
ostringstream error;
solidity::SourceReferenceFormatter::printExceptionInformation(error, exception, "Error", compiler);
cwarn << "Solidity compilation error: " << error.str();
return Json::Value(Json::objectValue);
}
catch (...)
{
cwarn << "Uncought solidity compilation exception";
return Json::Value(Json::objectValue);
}
#else
(void)_source;
#endif
return res;
}
string WebThreeStubServerBase::eth_newFilter(Json::Value const& _json)
{
try
{
return toJS(client()->installWatch(toLogFilter(_json, *client())));
}
catch (...)
{
BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS));
}
}
string WebThreeStubServerBase::eth_newFilterEx(Json::Value const& _json)
{
try
{
@ -823,6 +913,22 @@ Json::Value WebThreeStubServerBase::eth_getFilterChanges(string const& _filterId
}
}
Json::Value WebThreeStubServerBase::eth_getFilterChangesEx(string const& _filterId)
{
try
{
int id = jsToInt(_filterId);
auto entries = client()->checkWatch(id);
if (entries.size())
cnote << "FIRING WATCH" << id << entries.size();
return toJson(entries);
}
catch (...)
{
BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS));
}
}
Json::Value WebThreeStubServerBase::eth_getFilterLogs(string const& _filterId)
{
try
@ -835,6 +941,18 @@ Json::Value WebThreeStubServerBase::eth_getFilterLogs(string const& _filterId)
}
}
Json::Value WebThreeStubServerBase::eth_getFilterLogsEx(string const& _filterId)
{
try
{
return toJson(client()->logs(jsToInt(_filterId)));
}
catch (...)
{
BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS));
}
}
Json::Value WebThreeStubServerBase::eth_getLogs(Json::Value const& _json)
{
try
@ -980,7 +1098,6 @@ string WebThreeStubServerBase::shh_addToGroup(string const& _group, string const
string WebThreeStubServerBase::shh_newFilter(Json::Value const& _json)
{
try
{
pair<shh::Topics, Public> w = toWatch(_json);

7
libweb3jsonrpc/WebThreeStubServerBase.h

@ -60,8 +60,6 @@ public:
/**
* @brief JSON-RPC api implementation
* @todo filters should work on unsigned instead of int
* unsigned are not supported in json-rpc-cpp and there are bugs with double in json-rpc-cpp version 0.2.1
* @todo split these up according to subprotocol (eth, shh, db, p2p, web3) and make it /very/ clear about how to add other subprotocols.
* @todo modularise everything so additional subprotocols don't need to change this file.
*/
@ -105,13 +103,16 @@ public:
virtual Json::Value eth_getCompilers();
virtual std::string eth_compileLLL(std::string const& _s);
virtual std::string eth_compileSerpent(std::string const& _s);
virtual std::string eth_compileSolidity(std::string const& _code);
virtual Json::Value eth_compileSolidity(std::string const& _code);
virtual std::string eth_newFilter(Json::Value const& _json);
virtual std::string eth_newFilterEx(Json::Value const& _json);
virtual std::string eth_newBlockFilter();
virtual std::string eth_newPendingTransactionFilter();
virtual bool eth_uninstallFilter(std::string const& _filterId);
virtual Json::Value eth_getFilterChanges(std::string const& _filterId);
virtual Json::Value eth_getFilterChangesEx(std::string const& _filterId);
virtual Json::Value eth_getFilterLogs(std::string const& _filterId);
virtual Json::Value eth_getFilterLogsEx(std::string const& _filterId);
virtual Json::Value eth_getLogs(Json::Value const& _json);
virtual Json::Value eth_getWork();
virtual bool eth_submitWork(std::string const& _nonce, std::string const&, std::string const& _mixHash);

22
libweb3jsonrpc/abstractwebthreestubserver.h

@ -45,13 +45,16 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServer<AbstractWebThr
this->bindAndAddMethod(jsonrpc::Procedure("eth_getCompilers", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, NULL), &AbstractWebThreeStubServer::eth_getCompilersI);
this->bindAndAddMethod(jsonrpc::Procedure("eth_compileLLL", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_compileLLLI);
this->bindAndAddMethod(jsonrpc::Procedure("eth_compileSerpent", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_compileSerpentI);
this->bindAndAddMethod(jsonrpc::Procedure("eth_compileSolidity", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_compileSolidityI);
this->bindAndAddMethod(jsonrpc::Procedure("eth_compileSolidity", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_compileSolidityI);
this->bindAndAddMethod(jsonrpc::Procedure("eth_newFilter", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_OBJECT, NULL), &AbstractWebThreeStubServer::eth_newFilterI);
this->bindAndAddMethod(jsonrpc::Procedure("eth_newFilterEx", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_OBJECT, NULL), &AbstractWebThreeStubServer::eth_newFilterExI);
this->bindAndAddMethod(jsonrpc::Procedure("eth_newBlockFilter", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_newBlockFilterI);
this->bindAndAddMethod(jsonrpc::Procedure("eth_newPendingTransactionFilter", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_newPendingTransactionFilterI);
this->bindAndAddMethod(jsonrpc::Procedure("eth_uninstallFilter", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_uninstallFilterI);
this->bindAndAddMethod(jsonrpc::Procedure("eth_getFilterChanges", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_getFilterChangesI);
this->bindAndAddMethod(jsonrpc::Procedure("eth_getFilterChangesEx", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_getFilterChangesExI);
this->bindAndAddMethod(jsonrpc::Procedure("eth_getFilterLogs", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_getFilterLogsI);
this->bindAndAddMethod(jsonrpc::Procedure("eth_getFilterLogsEx", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_getFilterLogsExI);
this->bindAndAddMethod(jsonrpc::Procedure("eth_getLogs", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_OBJECT, NULL), &AbstractWebThreeStubServer::eth_getLogsI);
this->bindAndAddMethod(jsonrpc::Procedure("eth_getWork", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, NULL), &AbstractWebThreeStubServer::eth_getWorkI);
this->bindAndAddMethod(jsonrpc::Procedure("eth_submitWork", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING,"param3",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_submitWorkI);
@ -227,6 +230,10 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServer<AbstractWebThr
{
response = this->eth_newFilter(request[0u]);
}
inline virtual void eth_newFilterExI(const Json::Value &request, Json::Value &response)
{
response = this->eth_newFilterEx(request[0u]);
}
inline virtual void eth_newBlockFilterI(const Json::Value &request, Json::Value &response)
{
(void)request;
@ -245,10 +252,18 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServer<AbstractWebThr
{
response = this->eth_getFilterChanges(request[0u].asString());
}
inline virtual void eth_getFilterChangesExI(const Json::Value &request, Json::Value &response)
{
response = this->eth_getFilterChangesEx(request[0u].asString());
}
inline virtual void eth_getFilterLogsI(const Json::Value &request, Json::Value &response)
{
response = this->eth_getFilterLogs(request[0u].asString());
}
inline virtual void eth_getFilterLogsExI(const Json::Value &request, Json::Value &response)
{
response = this->eth_getFilterLogsEx(request[0u].asString());
}
inline virtual void eth_getLogsI(const Json::Value &request, Json::Value &response)
{
response = this->eth_getLogs(request[0u]);
@ -364,13 +379,16 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServer<AbstractWebThr
virtual Json::Value eth_getCompilers() = 0;
virtual std::string eth_compileLLL(const std::string& param1) = 0;
virtual std::string eth_compileSerpent(const std::string& param1) = 0;
virtual std::string eth_compileSolidity(const std::string& param1) = 0;
virtual Json::Value eth_compileSolidity(const std::string& param1) = 0;
virtual std::string eth_newFilter(const Json::Value& param1) = 0;
virtual std::string eth_newFilterEx(const Json::Value& param1) = 0;
virtual std::string eth_newBlockFilter() = 0;
virtual std::string eth_newPendingTransactionFilter() = 0;
virtual bool eth_uninstallFilter(const std::string& param1) = 0;
virtual Json::Value eth_getFilterChanges(const std::string& param1) = 0;
virtual Json::Value eth_getFilterChangesEx(const std::string& param1) = 0;
virtual Json::Value eth_getFilterLogs(const std::string& param1) = 0;
virtual Json::Value eth_getFilterLogsEx(const std::string& param1) = 0;
virtual Json::Value eth_getLogs(const Json::Value& param1) = 0;
virtual Json::Value eth_getWork() = 0;
virtual bool eth_submitWork(const std::string& param1, const std::string& param2, const std::string& param3) = 0;

5
libweb3jsonrpc/spec.json

@ -34,13 +34,16 @@
{ "name": "eth_getCompilers", "params": [], "order": [], "returns": []},
{ "name": "eth_compileLLL", "params": [""], "order": [], "returns": ""},
{ "name": "eth_compileSerpent", "params": [""], "order": [], "returns": ""},
{ "name": "eth_compileSolidity", "params": [""], "order": [], "returns": ""},
{ "name": "eth_compileSolidity", "params": [""], "order": [], "returns": {}},
{ "name": "eth_newFilter", "params": [{}], "order": [], "returns": ""},
{ "name": "eth_newFilterEx", "params": [{}], "order": [], "returns": ""},
{ "name": "eth_newBlockFilter", "params": [], "order": [], "returns": ""},
{ "name": "eth_newPendingTransactionFilter", "params": [], "order": [], "returns": ""},
{ "name": "eth_uninstallFilter", "params": [""], "order": [], "returns": true},
{ "name": "eth_getFilterChanges", "params": [""], "order": [], "returns": []},
{ "name": "eth_getFilterChangesEx", "params": [""], "order": [], "returns": []},
{ "name": "eth_getFilterLogs", "params": [""], "order": [], "returns": []},
{ "name": "eth_getFilterLogsEx", "params": [""], "order": [], "returns": []},
{ "name": "eth_getLogs", "params": [{}], "order": [], "returns": []},
{ "name": "eth_getWork", "params": [], "order": [], "returns": []},
{ "name": "eth_submitWork", "params": ["", "", ""], "order": [], "returns": true},

9
mix/MixClient.cpp

@ -83,6 +83,11 @@ void MixClient::resetState(std::unordered_map<Address, Account> const& _accounts
WriteGuard l(x_state);
Guard fl(x_filtersWatches);
m_filters.clear();
for (auto& i: m_specialFilters)
i.second.clear();
m_watches.clear();
m_stateDB = OverlayDB();
SecureTrieDB<Address, MemoryDB> accountState(&m_stateDB);
accountState.init();
@ -254,11 +259,11 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c
LocalisedLogEntries logs;
TransactionReceipt const& tr = _state.receipt(_state.pending().size() - 1);
auto trHash = _state.pending().at(_state.pending().size() - 1).sha3();
//auto trHash = _state.pending().at(_state.pending().size() - 1).sha3();
LogEntries le = tr.log();
if (le.size())
for (unsigned j = 0; j < le.size(); ++j)
logs.insert(logs.begin(), LocalisedLogEntry(le[j], bc().number() + 1, trHash));
logs.insert(logs.begin(), LocalisedLogEntry(le[j]));
d.logs = logs;
}
WriteGuard l(x_executions);

10
test/TestHelper.cpp

@ -344,6 +344,16 @@ void ImportTest::exportTest(bytes const& _output, State const& _statePost)
m_TestObject["out"] = (_output.size() > 4096 && !Options::get().fulloutput) ? "#" + toString(_output.size()) : toHex(_output, 2, HexPrefix::Add);
// compare expected output with post output
if (m_TestObject.count("expectOut") > 0)
{
std::string warning = "Check State: Error! Unexpected output: " + m_TestObject["out"].get_str() + " Expected: " + m_TestObject["expectOut"].get_str();
if (Options::get().checkState)
BOOST_CHECK_MESSAGE((m_TestObject["out"].get_str() == m_TestObject["expectOut"].get_str()), warning);
else
BOOST_WARN_MESSAGE((m_TestObject["out"].get_str() == m_TestObject["expectOut"].get_str()), warning);
}
// export logs
m_TestObject["logs"] = exportLog(_statePost.pending().size() ? _statePost.log(0) : LogEntries());

3
test/libethereum/StateTestsFiller/stPreCompiledContractsFiller.json

@ -3644,6 +3644,5 @@
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"data" : ""
}
},
}
}

854
test/libethereum/StateTestsFiller/stPrecompiledContractsTransactionFiller.json

File diff suppressed because one or more lines are too long

5
test/libethereum/state.cpp

@ -129,6 +129,11 @@ BOOST_AUTO_TEST_CASE(stPreCompiledContracts)
dev::test::executeTests("stPreCompiledContracts", "/StateTests",dev::test::getFolder(__FILE__) + "/StateTestsFiller", dev::test::doStateTests);
}
BOOST_AUTO_TEST_CASE(stPrecompiledContractsTransaction)
{
dev::test::executeTests("stPrecompiledContractsTransaction", "/StateTests",dev::test::getFolder(__FILE__) + "/StateTestsFiller", dev::test::doStateTests);
}
BOOST_AUTO_TEST_CASE(stLogTests)
{
dev::test::executeTests("stLogTests", "/StateTests",dev::test::getFolder(__FILE__) + "/StateTestsFiller", dev::test::doStateTests);

4
test/libsolidity/Assembly.cpp

@ -105,8 +105,8 @@ BOOST_AUTO_TEST_CASE(location_test)
shared_ptr<string const> n = make_shared<string>("source");
AssemblyItems items = compileContract(sourceCode);
vector<SourceLocation> locations =
vector<SourceLocation>(11, SourceLocation(2, 75, n)) +
vector<SourceLocation>(12, SourceLocation(20, 72, n)) +
vector<SourceLocation>(17, SourceLocation(2, 75, n)) +
vector<SourceLocation>(14, SourceLocation(20, 72, n)) +
vector<SourceLocation>{SourceLocation(42, 51, n), SourceLocation(65, 67, n)} +
vector<SourceLocation>(4, SourceLocation(58, 67, n)) +
vector<SourceLocation>(3, SourceLocation(20, 72, n));

93
test/libsolidity/SolidityEndToEndTest.cpp

@ -566,16 +566,16 @@ BOOST_AUTO_TEST_CASE(strings)
BOOST_AUTO_TEST_CASE(empty_string_on_stack)
{
char const* sourceCode = "contract test {\n"
" function run(bytes0 empty, uint8 inp) returns(uint16 a, bytes0 b, bytes4 c) {\n"
" var x = \"abc\";\n"
" var y = \"\";\n"
" var z = inp;\n"
" a = z; b = y; c = x;"
" }\n"
"}\n";
char const* sourceCode = R"(
contract test {
function run() external returns(bytes2 ret) {
var y = "";
ret = y;
}
}
)";
compileAndRun(sourceCode);
BOOST_CHECK(callContractFunction("run(bytes0,uint8)", string(), byte(0x02)) == encodeArgs(0x2, string(""), string("abc\0")));
BOOST_CHECK(callContractFunction("run()") == encodeArgs(byte(0x00)));
}
BOOST_AUTO_TEST_CASE(inc_dec_operators)
@ -2396,7 +2396,7 @@ BOOST_AUTO_TEST_CASE(event_really_lots_of_data)
callContractFunction("deposit()");
BOOST_REQUIRE_EQUAL(m_logs.size(), 1);
BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress);
BOOST_CHECK(m_logs[0].data == encodeArgs(10, 4, 15) + FixedHash<4>(dev::sha3("deposit()")).asBytes());
BOOST_CHECK(m_logs[0].data == encodeArgs(10, 0x60, 15, 4) + FixedHash<4>(dev::sha3("deposit()")).asBytes());
BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1);
BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::sha3(string("Deposit(uint256,bytes,uint256)")));
}
@ -2420,7 +2420,7 @@ BOOST_AUTO_TEST_CASE(event_really_lots_of_data_from_storage)
callContractFunction("deposit()");
BOOST_REQUIRE_EQUAL(m_logs.size(), 1);
BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress);
BOOST_CHECK(m_logs[0].data == encodeArgs(10, 3, 15) + asBytes("ABC"));
BOOST_CHECK(m_logs[0].data == encodeArgs(10, 0x60, 15, 3) + asBytes("ABC"));
BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1);
BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::sha3(string("Deposit(uint256,bytes,uint256)")));
}
@ -2531,6 +2531,27 @@ BOOST_AUTO_TEST_CASE(sha3_with_bytes)
BOOST_CHECK(callContractFunction("foo()") == encodeArgs(true));
}
BOOST_AUTO_TEST_CASE(iterated_sha3_with_bytes)
{
char const* sourceCode = R"(
contract c {
bytes data;
function foo() returns (bytes32)
{
data.length = 3;
data[0] = "x";
data[1] = "y";
data[2] = "z";
return sha3("b", sha3(data), "a");
}
}
)";
compileAndRun(sourceCode);
BOOST_CHECK(callContractFunction("foo()") == encodeArgs(
u256(dev::sha3(bytes{'b'} + dev::sha3("xyz").asBytes() + bytes{'a'}))
));
}
BOOST_AUTO_TEST_CASE(generic_call)
{
char const* sourceCode = R"**(
@ -3786,30 +3807,6 @@ BOOST_AUTO_TEST_CASE(packed_storage_structs_delete)
BOOST_CHECK(m_state.storage(m_contractAddress).empty());
}
BOOST_AUTO_TEST_CASE(packed_storage_structs_with_bytes0)
{
char const* sourceCode = R"(
contract C {
struct str { uint8 a; bytes0 b; uint8 c; }
uint8 a;
bytes0 x;
uint8 b;
str data;
function test() returns (bool) {
a = 2;
b = 3;
data.a = 4;
data.c = 5;
delete x;
delete data.b;
return a == 2 && b == 3 && data.a == 4 && data.c == 5;
}
}
)";
compileAndRun(sourceCode);
BOOST_CHECK(callContractFunction("test()") == encodeArgs(true));
}
BOOST_AUTO_TEST_CASE(overloaded_function_call_resolve_to_first)
{
char const* sourceCode = R"(
@ -4209,6 +4206,32 @@ BOOST_AUTO_TEST_CASE(failing_send)
BOOST_REQUIRE(callContractFunction("callHelper(address)", c_helperAddress) == encodeArgs(true, 20));
}
BOOST_AUTO_TEST_CASE(reusing_memory)
{
// Invoke some features that use memory and test that they do not interfere with each other.
char const* sourceCode = R"(
contract Helper {
uint public flag;
function Helper(uint x) {
flag = x;
}
}
contract Main {
mapping(uint => uint) map;
function f(uint x) returns (uint) {
map[x] = x;
return (new Helper(uint(sha3(this.g(map[x]))))).flag();
}
function g(uint a) returns (uint)
{
return map[a];
}
}
)";
compileAndRun(sourceCode, 0, "Main");
BOOST_REQUIRE(callContractFunction("f(uint256)", 0x34) == encodeArgs(dev::sha3(dev::toBigEndian(u256(0x34)))));
}
BOOST_AUTO_TEST_SUITE_END()
}

21
test/libsolidity/SolidityNameAndTypeResolution.cpp

@ -558,16 +558,6 @@ BOOST_AUTO_TEST_CASE(function_external_call_not_allowed_conversion)
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
}
// todo delete when implemented
BOOST_AUTO_TEST_CASE(arrays_in_internal_functions)
{
char const* text = R"(
contract Test {
function foo(address[] addresses) {}
})";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
}
BOOST_AUTO_TEST_CASE(function_internal_allowed_conversion)
{
char const* text = R"(
@ -1579,7 +1569,6 @@ BOOST_AUTO_TEST_CASE(test_fromElementaryTypeName)
BOOST_CHECK(*Type::fromElementaryTypeName(Token::UInt256) == *make_shared<IntegerType>(256, IntegerType::Modifier::Unsigned));
BOOST_CHECK(*Type::fromElementaryTypeName(Token::Byte) == *make_shared<FixedBytesType>(1));
BOOST_CHECK(*Type::fromElementaryTypeName(Token::Bytes0) == *make_shared<FixedBytesType>(0));
BOOST_CHECK(*Type::fromElementaryTypeName(Token::Bytes1) == *make_shared<FixedBytesType>(1));
BOOST_CHECK(*Type::fromElementaryTypeName(Token::Bytes2) == *make_shared<FixedBytesType>(2));
BOOST_CHECK(*Type::fromElementaryTypeName(Token::Bytes3) == *make_shared<FixedBytesType>(3));
@ -1666,16 +1655,6 @@ BOOST_AUTO_TEST_CASE(local_const_variable)
BOOST_CHECK_THROW(parseTextAndResolveNames(text), ParserError);
}
BOOST_AUTO_TEST_CASE(bytes0_array)
{
char const* text = R"(
contract Foo {
bytes0[] illegalArray;
}
)";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
}
BOOST_AUTO_TEST_CASE(overloaded_function_cannot_resolve)
{
char const* sourceCode = R"(

491
test/libsolidity/SolidityWallet.cpp

@ -0,0 +1,491 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2015
* Tests for a (comparatively) complex multisig wallet contract.
*/
#include <string>
#include <tuple>
#include <boost/test/unit_test.hpp>
#include <libdevcore/Hash.h>
#include <test/libsolidity/solidityExecutionFramework.h>
using namespace std;
namespace dev
{
namespace solidity
{
namespace test
{
static char const* walletCode = R"DELIMITER(
//sol Wallet
// Multi-sig, daily-limited account proxy/wallet.
// @authors:
// Gav Wood <g@ethdev.com>
// inheritable "property" contract that enables methods to be protected by requiring the acquiescence of either a
// single, or, crucially, each of a number of, designated owners.
// usage:
// use modifiers onlyowner (just own owned) or onlymanyowners(hash), whereby the same hash must be provided by
// some number (specified in constructor) of the set of owners (specified in the constructor, modifiable) before the
// interior is executed.
contract multiowned {
// struct for the status of a pending operation.
struct PendingState {
uint yetNeeded;
uint ownersDone;
uint index;
}
// this contract only has five types of events: it can accept a confirmation, in which case
// we record owner and operation (hash) alongside it.
event Confirmation(address owner, bytes32 operation);
event Revoke(address owner, bytes32 operation);
// some others are in the case of an owner changing.
event OwnerChanged(address oldOwner, address newOwner);
event OwnerAdded(address newOwner);
event OwnerRemoved(address oldOwner);
// the last one is emitted if the required signatures change
event RequirementChanged(uint newRequirement);
// constructor is given number of sigs required to do protected "onlymanyowners" transactions
// as well as the selection of addresses capable of confirming them.
function multiowned() {
m_required = 1;
m_numOwners = 1;
m_owners[m_numOwners] = uint(msg.sender);
m_ownerIndex[uint(msg.sender)] = m_numOwners;
}
// simple single-sig function modifier.
modifier onlyowner {
if (isOwner(msg.sender))
_
}
// multi-sig function modifier: the operation must have an intrinsic hash in order
// that later attempts can be realised as the same underlying operation and
// thus count as confirmations.
modifier onlymanyowners(bytes32 _operation) {
if (confirmed(_operation))
_
}
// Revokes a prior confirmation of the given operation
function revoke(bytes32 _operation) external {
uint ownerIndex = m_ownerIndex[uint(msg.sender)];
// make sure they're an owner
if (ownerIndex == 0) return;
uint ownerIndexBit = 2**ownerIndex;
var pending = m_pending[_operation];
if (pending.ownersDone & ownerIndexBit > 0) {
pending.yetNeeded++;
pending.ownersDone -= ownerIndexBit;
Revoke(msg.sender, _operation);
}
}
function confirmed(bytes32 _operation) internal returns (bool) {
// determine what index the present sender is:
uint ownerIndex = m_ownerIndex[uint(msg.sender)];
// make sure they're an owner
if (ownerIndex == 0) return;
var pending = m_pending[_operation];
// if we're not yet working on this operation, switch over and reset the confirmation status.
if (pending.yetNeeded == 0) {
// reset count of confirmations needed.
pending.yetNeeded = m_required;
// reset which owners have confirmed (none) - set our bitmap to 0.
pending.ownersDone = 0;
pending.index = m_pendingIndex.length++;
m_pendingIndex[pending.index] = _operation;
}
// determine the bit to set for this owner.
uint ownerIndexBit = 2**ownerIndex;
// make sure we (the message sender) haven't confirmed this operation previously.
if (pending.ownersDone & ownerIndexBit == 0) {
Confirmation(msg.sender, _operation);
// ok - check if count is enough to go ahead.
if (pending.yetNeeded <= 1) {
// enough confirmations: reset and run interior.
delete m_pendingIndex[m_pending[_operation].index];
delete m_pending[_operation];
return true;
}
else
{
// not enough: record that this owner in particular confirmed.
pending.yetNeeded--;
pending.ownersDone |= ownerIndexBit;
}
}
}
// Replaces an owner `_from` with another `_to`.
function changeOwner(address _from, address _to) onlymanyowners(sha3(msg.data)) external {
if (isOwner(_to)) return;
uint ownerIndex = m_ownerIndex[uint(_from)];
if (ownerIndex == 0) return;
clearPending();
m_owners[ownerIndex] = uint(_to);
m_ownerIndex[uint(_from)] = 0;
m_ownerIndex[uint(_to)] = ownerIndex;
OwnerChanged(_from, _to);
}
function addOwner(address _owner) onlymanyowners(sha3(msg.data)) external {
if (isOwner(_owner)) return;
clearPending();
if (m_numOwners >= c_maxOwners)
reorganizeOwners();
if (m_numOwners >= c_maxOwners)
return;
m_numOwners++;
m_owners[m_numOwners] = uint(_owner);
m_ownerIndex[uint(_owner)] = m_numOwners;
OwnerAdded(_owner);
}
function removeOwner(address _owner) onlymanyowners(sha3(msg.data)) external {
uint ownerIndex = m_ownerIndex[uint(_owner)];
if (ownerIndex == 0) return;
if (m_required > m_numOwners - 1) return;
m_owners[ownerIndex] = 0;
m_ownerIndex[uint(_owner)] = 0;
clearPending();
reorganizeOwners(); //make sure m_numOwner is equal to the number of owners and always points to the optimal free slot
OwnerRemoved(_owner);
}
function reorganizeOwners() private returns (bool) {
uint free = 1;
while (free < m_numOwners)
{
while (free < m_numOwners && m_owners[free] != 0) free++;
while (m_numOwners > 1 && m_owners[m_numOwners] == 0) m_numOwners--;
if (free < m_numOwners && m_owners[m_numOwners] != 0 && m_owners[free] == 0)
{
m_owners[free] = m_owners[m_numOwners];
m_ownerIndex[m_owners[free]] = free;
m_owners[m_numOwners] = 0;
}
}
}
function clearPending() internal {
uint length = m_pendingIndex.length;
for (uint i = 0; i < length; ++i)
if (m_pendingIndex[i] != 0)
delete m_pending[m_pendingIndex[i]];
delete m_pendingIndex;
}
function changeRequirement(uint _newRequired) onlymanyowners(sha3(msg.data)) external {
if (_newRequired > m_numOwners) return;
m_required = _newRequired;
clearPending();
RequirementChanged(_newRequired);
}
function isOwner(address _addr) returns (bool) {
return m_ownerIndex[uint(_addr)] > 0;
}
// the number of owners that must confirm the same operation before it is run.
uint m_required;
// pointer used to find a free slot in m_owners
uint m_numOwners;
// list of owners
uint[256] m_owners;
uint constant c_maxOwners = 250;
// index on the list of owners to allow reverse lookup
mapping(uint => uint) m_ownerIndex;
// the ongoing operations.
mapping(bytes32 => PendingState) m_pending;
bytes32[] m_pendingIndex;
}
// inheritable "property" contract that enables methods to be protected by placing a linear limit (specifiable)
// on a particular resource per calendar day. is multiowned to allow the limit to be altered. resource that method
// uses is specified in the modifier.
contract daylimit is multiowned {
// constructor - just records the present day's index.
function daylimit() {
m_lastDay = today();
}
// (re)sets the daily limit. needs many of the owners to confirm. doesn't alter the amount already spent today.
function setDailyLimit(uint _newLimit) onlymanyowners(sha3(msg.data)) external {
m_dailyLimit = _newLimit;
}
// (re)sets the daily limit. needs many of the owners to confirm. doesn't alter the amount already spent today.
function resetSpentToday() onlymanyowners(sha3(msg.data)) external {
m_spentToday = 0;
}
// checks to see if there is at least `_value` left from the daily limit today. if there is, subtracts it and
// returns true. otherwise just returns false.
function underLimit(uint _value) internal onlyowner returns (bool) {
// reset the spend limit if we're on a different day to last time.
if (today() > m_lastDay) {
m_spentToday = 0;
m_lastDay = today();
}
// check to see if there's enough left - if so, subtract and return true.
if (m_spentToday + _value >= m_spentToday && m_spentToday + _value <= m_dailyLimit) {
m_spentToday += _value;
return true;
}
return false;
}
// simple modifier for daily limit.
modifier limitedDaily(uint _value) {
if (underLimit(_value))
_
}
// determines today's index.
function today() private constant returns (uint) { return now / 1 days; }
uint m_spentToday;
uint m_dailyLimit;
uint m_lastDay;
}
// interface contract for multisig proxy contracts; see below for docs.
contract multisig {
event Deposit(address from, uint value);
event SingleTransact(address owner, uint value, address to, bytes data);
event MultiTransact(address owner, bytes32 operation, uint value, address to, bytes data);
event ConfirmationNeeded(bytes32 operation, address initiator, uint value, address to, bytes data);
function changeOwner(address _from, address _to) external;
function execute(address _to, uint _value, bytes _data) external returns (bytes32);
function confirm(bytes32 _h) returns (bool);
}
// usage:
// bytes32 h = Wallet(w).from(oneOwner).transact(to, value, data);
// Wallet(w).from(anotherOwner).confirm(h);
contract Wallet is multisig, multiowned, daylimit {
// Transaction structure to remember details of transaction lest it need be saved for a later call.
struct Transaction {
address to;
uint value;
bytes data;
}
// logged events:
// Funds has arrived into the wallet (record how much).
event Deposit(address from, uint value);
// Single transaction going out of the wallet (record who signed for it, how much, and to whom it's going).
event SingleTransact(address owner, uint value, address to, bytes data);
// constructor - just pass on the owner arra to the multiowned.
event Created();
function Wallet() {
Created();
}
// kills the contract sending everything to `_to`.
function kill(address _to) onlymanyowners(sha3(msg.data)) external {
suicide(_to);
}
// gets called when no other function matches
function() {
// just being sent some cash?
if (msg.value > 0)
Deposit(msg.sender, msg.value);
}
// Outside-visible transact entry point. Executes transacion immediately if below daily spend limit.
// If not, goes into multisig process. We provide a hash on return to allow the sender to provide
// shortcuts for the other confirmations (allowing them to avoid replicating the _to, _value
// and _data arguments). They still get the option of using them if they want, anyways.
function execute(address _to, uint _value, bytes _data) onlyowner external returns (bytes32 _r) {
// first, take the opportunity to check that we're under the daily limit.
if (underLimit(_value)) {
SingleTransact(msg.sender, _value, _to, _data);
// yes - just execute the call.
_to.call.value(_value)(_data);
return 0;
}
// determine our operation hash.
_r = sha3(msg.data);
if (!confirm(_r) && m_txs[_r].to == 0) {
m_txs[_r].to = _to;
m_txs[_r].value = _value;
m_txs[_r].data = _data;
ConfirmationNeeded(_r, msg.sender, _value, _to, _data);
}
}
// confirm a transaction through just the hash. we use the previous transactions map, m_txs, in order
// to determine the body of the transaction from the hash provided.
function confirm(bytes32 _h) onlymanyowners(_h) returns (bool) {
if (m_txs[_h].to != 0) {
m_txs[_h].to.call.value(m_txs[_h].value)(m_txs[_h].data);
MultiTransact(msg.sender, _h, m_txs[_h].value, m_txs[_h].to, m_txs[_h].data);
delete m_txs[_h];
return true;
}
}
function clearPending() internal {
uint length = m_pendingIndex.length;
for (uint i = 0; i < length; ++i)
delete m_txs[m_pendingIndex[i]];
super.clearPending();
}
// pending transactions we have at present.
mapping (bytes32 => Transaction) m_txs;
}
)DELIMITER";
static unique_ptr<bytes> s_compiledWallet;
class WalletTestFramework: public ExecutionFramework
{
protected:
void deployWallet(u256 const& _value = 0)
{
if (!s_compiledWallet)
{
m_optimize = true;
m_compiler.reset(false, m_addStandardSources);
m_compiler.addSource("", walletCode);
ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(m_optimize, m_optimizeRuns), "Compiling contract failed");
s_compiledWallet.reset(new bytes(m_compiler.getBytecode("Wallet")));
}
sendMessage(*s_compiledWallet, true, _value);
BOOST_REQUIRE(!m_output.empty());
}
};
/// This is a test suite that tests optimised code!
BOOST_FIXTURE_TEST_SUITE(SolidityWallet, WalletTestFramework)
BOOST_AUTO_TEST_CASE(creation)
{
deployWallet(200);
BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(m_sender, h256::AlignRight)) == encodeArgs(true));
BOOST_REQUIRE(callContractFunction("isOwner(address)", ~h256(m_sender, h256::AlignRight)) == encodeArgs(false));
}
BOOST_AUTO_TEST_CASE(add_owners)
{
deployWallet(200);
Address originalOwner = m_sender;
BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x12)) == encodeArgs());
BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x12)) == encodeArgs(true));
// now let the new owner add someone
m_sender = Address(0x12);
BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x13)) == encodeArgs());
BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x13)) == encodeArgs(true));
// and check that a non-owner cannot add a new owner
m_sender = Address(0x50);
BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x20)) == encodeArgs());
BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x20)) == encodeArgs(false));
// finally check that all the owners are there
BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(originalOwner, h256::AlignRight)) == encodeArgs(true));
BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x12)) == encodeArgs(true));
BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x13)) == encodeArgs(true));
}
BOOST_AUTO_TEST_CASE(change_owners)
{
deployWallet(200);
BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x12)) == encodeArgs());
BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x12)) == encodeArgs(true));
BOOST_REQUIRE(callContractFunction("changeOwner(address,address)", h256(0x12), h256(0x13)) == encodeArgs());
BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x12)) == encodeArgs(false));
BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x13)) == encodeArgs(true));
}
BOOST_AUTO_TEST_CASE(remove_owner)
{
deployWallet(200);
// add 10 owners
for (unsigned i = 0; i < 10; ++i)
{
BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x12 + i)) == encodeArgs());
BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x12 + i)) == encodeArgs(true));
}
// check they are there again
for (unsigned i = 0; i < 10; ++i)
BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x12 + i)) == encodeArgs(true));
// remove the odd owners
for (unsigned i = 0; i < 10; ++i)
if (i % 2 == 1)
BOOST_REQUIRE(callContractFunction("removeOwner(address)", h256(0x12 + i)) == encodeArgs());
// check the result
for (unsigned i = 0; i < 10; ++i)
BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x12 + i)) == encodeArgs(i % 2 == 0));
// add them again
for (unsigned i = 0; i < 10; ++i)
if (i % 2 == 1)
BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x12 + i)) == encodeArgs());
// check everyone is there
for (unsigned i = 0; i < 10; ++i)
BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x12 + i)) == encodeArgs(true));
}
BOOST_AUTO_TEST_CASE(multisig_value_transfer)
{
deployWallet(200);
BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x12)) == encodeArgs());
BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x13)) == encodeArgs());
BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x14)) == encodeArgs());
// 4 owners, set required to 3
BOOST_REQUIRE(callContractFunction("changeRequirement(uint256)", u256(3)) == encodeArgs());
// check that balance is and stays zero at destination address
h256 opHash("f916231db11c12e0142dc51f23632bc655de87c63f83fc928c443e90f7aa364a");
BOOST_CHECK_EQUAL(m_state.balance(Address(0x05)), 0);
m_sender = Address(0x12);
BOOST_REQUIRE(callContractFunction("execute(address,uint256,bytes)", h256(0x05), 100, 0x60, 0x00) == encodeArgs(opHash));
BOOST_CHECK_EQUAL(m_state.balance(Address(0x05)), 0);
m_sender = Address(0x13);
BOOST_REQUIRE(callContractFunction("execute(address,uint256,bytes)", h256(0x05), 100, 0x60, 0x00) == encodeArgs(opHash));
BOOST_CHECK_EQUAL(m_state.balance(Address(0x05)), 0);
m_sender = Address(0x14);
BOOST_REQUIRE(callContractFunction("execute(address,uint256,bytes)", h256(0x05), 100, 0x60, 0x00) == encodeArgs(opHash));
// now it should go through
BOOST_CHECK_EQUAL(m_state.balance(Address(0x05)), 100);
}
BOOST_AUTO_TEST_CASE(daylimit)
{
deployWallet(200);
BOOST_REQUIRE(callContractFunction("setDailyLimit(uint256)", h256(100)) == encodeArgs());
BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x12)) == encodeArgs());
BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x13)) == encodeArgs());
BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x14)) == encodeArgs());
// 4 owners, set required to 3
BOOST_REQUIRE(callContractFunction("changeRequirement(uint256)", u256(3)) == encodeArgs());
// try to send tx over daylimit
BOOST_CHECK_EQUAL(m_state.balance(Address(0x05)), 0);
m_sender = Address(0x12);
BOOST_REQUIRE(
callContractFunction("execute(address,uint256,bytes)", h256(0x05), 150, 0x60, 0x00) !=
encodeArgs(u256(0))
);
BOOST_CHECK_EQUAL(m_state.balance(Address(0x05)), 0);
// try to send tx under daylimit by stranger
m_sender = Address(0x77);
BOOST_REQUIRE(
callContractFunction("execute(address,uint256,bytes)", h256(0x05), 90, 0x60, 0x00) ==
encodeArgs(u256(0))
);
BOOST_CHECK_EQUAL(m_state.balance(Address(0x05)), 0);
// now send below limit by owner
m_sender = Address(0x12);
BOOST_REQUIRE(
callContractFunction("execute(address,uint256,bytes)", h256(0x05), 90, 0x60, 0x00) ==
encodeArgs(u256(0))
);
BOOST_CHECK_EQUAL(m_state.balance(Address(0x05)), 90);
}
//@todo test data calls
BOOST_AUTO_TEST_SUITE_END()
}
}
} // end namespaces

36
test/libweb3jsonrpc/webthreestubclient.h

@ -354,13 +354,13 @@ class WebThreeStubClient : public jsonrpc::Client
else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
}
std::string eth_compileSolidity(const std::string& param1) throw (jsonrpc::JsonRpcException)
Json::Value eth_compileSolidity(const std::string& param1) throw (jsonrpc::JsonRpcException)
{
Json::Value p;
p.append(param1);
Json::Value result = this->CallMethod("eth_compileSolidity",p);
if (result.isString())
return result.asString();
if (result.isObject())
return result;
else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
}
@ -374,6 +374,16 @@ class WebThreeStubClient : public jsonrpc::Client
else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
}
std::string eth_newFilterEx(const Json::Value& param1) throw (jsonrpc::JsonRpcException)
{
Json::Value p;
p.append(param1);
Json::Value result = this->CallMethod("eth_newFilterEx",p);
if (result.isString())
return result.asString();
else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
}
std::string eth_newBlockFilter() throw (jsonrpc::JsonRpcException)
{
Json::Value p;
@ -414,6 +424,16 @@ class WebThreeStubClient : public jsonrpc::Client
else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
}
Json::Value eth_getFilterChangesEx(const std::string& param1) throw (jsonrpc::JsonRpcException)
{
Json::Value p;
p.append(param1);
Json::Value result = this->CallMethod("eth_getFilterChangesEx",p);
if (result.isArray())
return result;
else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
}
Json::Value eth_getFilterLogs(const std::string& param1) throw (jsonrpc::JsonRpcException)
{
Json::Value p;
@ -424,6 +444,16 @@ class WebThreeStubClient : public jsonrpc::Client
else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
}
Json::Value eth_getFilterLogsEx(const std::string& param1) throw (jsonrpc::JsonRpcException)
{
Json::Value p;
p.append(param1);
Json::Value result = this->CallMethod("eth_getFilterLogsEx",p);
if (result.isArray())
return result;
else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
}
Json::Value eth_getLogs(const Json::Value& param1) throw (jsonrpc::JsonRpcException)
{
Json::Value p;

Loading…
Cancel
Save