Browse Source

Merge branch 'develop' of github.com:ethereum/cpp-ethereum into develop

cl-refactor
Gav Wood 10 years ago
parent
commit
5ebc193154
  1. 8
      ethminer/MinerAux.h
  2. 43
      evmjit/libevmjit/Array.cpp
  3. 27
      evmjit/libevmjit/Cache.cpp
  4. 2
      libdevcore/Common.h
  5. 2
      libdevcore/Log.cpp
  6. 182
      libethash-cl/ethash_cl_miner.cpp
  7. 18
      libethash-cl/ethash_cl_miner.h
  8. 4
      libethcore/Common.h
  9. 9
      libethcore/Ethash.cpp
  10. 6
      libethcore/Ethash.h
  11. 4
      libethereum/BlockChain.cpp
  12. 1
      libethereum/BlockChain.h
  13. 183
      libethereum/BlockQueue.cpp
  14. 28
      libethereum/BlockQueue.h
  15. 78
      libethereum/Client.cpp
  16. 33
      libethereum/ClientBase.cpp
  17. 5
      libethereum/ClientBase.h
  18. 70
      libethereum/EthereumHost.cpp
  19. 3
      libethereum/EthereumHost.h
  20. 6
      libethereum/EthereumPeer.cpp
  21. 8
      libethereum/Executive.cpp
  22. 6
      libethereum/Executive.h
  23. 1
      libethereum/Interface.h
  24. 27
      libethereum/LogFilter.cpp
  25. 15
      libethereum/LogFilter.h
  26. 6
      libethereum/State.cpp
  27. 26
      libevm/ExtVMFace.h
  28. 2
      libjsqrc/ethereumjs/.jshintrc
  29. 7
      libjsqrc/ethereumjs/.versions
  30. 5
      libjsqrc/ethereumjs/bower.json
  31. 1877
      libjsqrc/ethereumjs/dist/web3-light.js
  32. 71
      libjsqrc/ethereumjs/dist/web3-light.js.map
  33. 4
      libjsqrc/ethereumjs/dist/web3-light.min.js
  34. 1879
      libjsqrc/ethereumjs/dist/web3.js
  35. 71
      libjsqrc/ethereumjs/dist/web3.js.map
  36. 4
      libjsqrc/ethereumjs/dist/web3.min.js
  37. 23
      libjsqrc/ethereumjs/example/contract.html
  38. 29
      libjsqrc/ethereumjs/example/event_inc.html
  39. 203
      libjsqrc/ethereumjs/example/icap.html
  40. 102
      libjsqrc/ethereumjs/example/namereg.html
  41. 76
      libjsqrc/ethereumjs/example/natspec_contract.html
  42. 2
      libjsqrc/ethereumjs/index.js
  43. 6
      libjsqrc/ethereumjs/lib/solidity/param.js
  44. 10
      libjsqrc/ethereumjs/lib/utils/config.js
  45. 39
      libjsqrc/ethereumjs/lib/utils/sha3.js
  46. 85
      libjsqrc/ethereumjs/lib/utils/utils.js
  47. 2
      libjsqrc/ethereumjs/lib/version.json
  48. 13
      libjsqrc/ethereumjs/lib/web3.js
  49. 3
      libjsqrc/ethereumjs/lib/web3/event.js
  50. 31
      libjsqrc/ethereumjs/lib/web3/function.js
  51. 108
      libjsqrc/ethereumjs/lib/web3/icap.js
  52. 46
      libjsqrc/ethereumjs/lib/web3/namereg.js
  53. 94
      libjsqrc/ethereumjs/lib/web3/transfer.js
  54. 2
      libjsqrc/ethereumjs/package.js
  55. 3
      libjsqrc/ethereumjs/package.json
  56. 4
      libjsqrc/ethereumjs/test/batch.js
  57. 2
      libjsqrc/ethereumjs/test/coder.decodeParam.js
  58. 2
      libjsqrc/ethereumjs/test/coder.encodeParam.js
  59. 308
      libjsqrc/ethereumjs/test/contract.js
  60. 17
      libjsqrc/ethereumjs/test/sha3.js
  61. 32
      libjsqrc/ethereumjs/test/utils.isIBAN.js
  62. 8
      libjsqrc/ethereumjs/test/utils.toWei.js
  63. 49
      libjsqrc/ethereumjs/test/web3.eth.sendIBANTransaction.js
  64. 16
      libjsqrc/ethereumjs/test/web3.sha3.js
  65. 22
      libsolidity/Compiler.cpp
  66. 146
      libweb3jsonrpc/WebThreeStubServerBase.cpp
  67. 7
      libweb3jsonrpc/WebThreeStubServerBase.h
  68. 22
      libweb3jsonrpc/abstractwebthreestubserver.h
  69. 5
      libweb3jsonrpc/spec.json
  70. 16
      mix/MixClient.cpp
  71. 10
      test/TestHelper.cpp
  72. 3
      test/libethereum/StateTestsFiller/stPreCompiledContractsFiller.json
  73. 854
      test/libethereum/StateTestsFiller/stPrecompiledContractsTransactionFiller.json
  74. 5
      test/libethereum/state.cpp
  75. 4
      test/libsolidity/Assembly.cpp
  76. 491
      test/libsolidity/SolidityWallet.cpp
  77. 36
      test/libweb3jsonrpc/webthreestubclient.h

8
ethminer/MinerAux.h

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

43
evmjit/libevmjit/Array.cpp

@ -9,8 +9,6 @@
#include "Runtime.h" #include "Runtime.h"
#include "Utils.h" #include "Utils.h"
#include <set> // DEBUG only
namespace dev namespace dev
{ {
namespace eth 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" extern "C"
{ {
using namespace dev::eth::jit;
EXPORT void* ext_realloc(void* _data, size_t _size) noexcept EXPORT void* ext_realloc(void* _data, size_t _size) noexcept
{ {
//std::cerr << "REALLOC: " << _data << " [" << _size << "]" << std::endl; return std::realloc(_data, _size);
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;
} }
EXPORT void ext_free(void* _data) noexcept EXPORT void ext_free(void* _data) noexcept
{ {
std::free(_data); 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 "Cache.h"
#include <mutex>
#include "preprocessor/llvm_includes_start.h" #include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/Module.h> #include <llvm/IR/Module.h>
#include <llvm/IR/LLVMContext.h> #include <llvm/IR/LLVMContext.h>
@ -23,6 +25,8 @@ namespace jit
namespace namespace
{ {
using Guard = std::lock_guard<std::mutex>;
std::mutex x_cacheMutex;
CacheMode g_mode; CacheMode g_mode;
llvm::MemoryBuffer* g_lastObject; llvm::MemoryBuffer* g_lastObject;
ExecutionEngineListener* g_listener; ExecutionEngineListener* g_listener;
@ -43,6 +47,9 @@ namespace
ObjectCache* Cache::getObjectCache(CacheMode _mode, ExecutionEngineListener* _listener) ObjectCache* Cache::getObjectCache(CacheMode _mode, ExecutionEngineListener* _listener)
{ {
static ObjectCache objectCache; static ObjectCache objectCache;
Guard g{x_cacheMutex};
g_mode = _mode; g_mode = _mode;
g_listener = _listener; g_listener = _listener;
return &objectCache; return &objectCache;
@ -50,6 +57,8 @@ ObjectCache* Cache::getObjectCache(CacheMode _mode, ExecutionEngineListener* _li
void Cache::clear() void Cache::clear()
{ {
Guard g{x_cacheMutex};
using namespace llvm::sys; using namespace llvm::sys;
llvm::SmallString<256> cachePath; llvm::SmallString<256> cachePath;
path::system_temp_directory(false, 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) 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 // TODO: Cache dir should be in one place
using namespace llvm::sys; using namespace llvm::sys;
llvm::SmallString<256> cachePath; 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) std::unique_ptr<llvm::Module> Cache::getObject(std::string const& id)
{ {
Guard g{x_cacheMutex};
if (g_mode != CacheMode::on && g_mode != CacheMode::read) if (g_mode != CacheMode::on && g_mode != CacheMode::read)
return nullptr; return nullptr;
if (g_listener) // TODO: Disabled because is not thread-safe.
g_listener->stateChanged(ExecState::CacheLoad); //if (g_listener)
// g_listener->stateChanged(ExecState::CacheLoad);
DLOG(cache) << id << ": search\n"; DLOG(cache) << id << ": search\n";
if (!CHECK(!g_lastObject)) 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) void ObjectCache::notifyObjectCompiled(llvm::Module const* _module, llvm::MemoryBuffer const* _object)
{ {
Guard g{x_cacheMutex};
// Only in "on" and "write" mode // Only in "on" and "write" mode
if (g_mode != CacheMode::on && g_mode != CacheMode::write) if (g_mode != CacheMode::on && g_mode != CacheMode::write)
return; return;
if (g_listener) // TODO: Disabled because is not thread-safe.
g_listener->stateChanged(ExecState::CacheWrite); // if (g_listener)
// g_listener->stateChanged(ExecState::CacheWrite);
auto&& id = _module->getModuleIdentifier(); auto&& id = _module->getModuleIdentifier();
llvm::SmallString<256> cachePath; 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) llvm::MemoryBuffer* ObjectCache::getObject(llvm::Module const* _module)
{ {
Guard g{x_cacheMutex};
DLOG(cache) << _module->getModuleIdentifier() << ": use\n"; DLOG(cache) << _module->getModuleIdentifier() << ": use\n";
auto o = g_lastObject; auto o = g_lastObject;
g_lastObject = nullptr; g_lastObject = nullptr;

2
libdevcore/Common.h

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

2
libdevcore/Log.cpp

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

182
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 // 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 #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]; char buf[256];
sprintf(buf, "#define %s %uu\n", id, value); sprintf(buf, "#define %s %uu\n", _id, _value);
source.insert(source.begin(), buf, buf + strlen(buf)); _source.insert(_source.begin(), buf, buf + strlen(buf));
} }
ethash_cl_miner::search_hook::~search_hook() {} ethash_cl_miner::search_hook::~search_hook() {}
@ -71,44 +71,44 @@ ethash_cl_miner::~ethash_cl_miner()
finish(); 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); cl::Platform::get(&platforms);
if (platforms.empty()) if (platforms.empty())
{ {
ETHCL_LOG("No OpenCL platforms found."); ETHCL_LOG("No OpenCL platforms found.");
return std::string(); return string();
} }
// get GPU device of the selected platform // get GPU device of the selected platform
std::vector<cl::Device> devices; vector<cl::Device> devices;
unsigned platform_num = std::min<unsigned>(_platformId, platforms.size() - 1); unsigned platform_num = min<unsigned>(_platformId, platforms.size() - 1);
platforms[platform_num].getDevices(CL_DEVICE_TYPE_ALL, &devices); platforms[platform_num].getDevices(CL_DEVICE_TYPE_ALL, &devices);
if (devices.empty()) if (devices.empty())
{ {
ETHCL_LOG("No OpenCL devices found."); ETHCL_LOG("No OpenCL devices found.");
return std::string(); return string();
} }
// use selected default device // 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]; 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 + "\" }"; 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); cl::Platform::get(&platforms);
return platforms.size(); 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); cl::Platform::get(&platforms);
if (platforms.empty()) if (platforms.empty())
{ {
@ -116,8 +116,8 @@ unsigned ethash_cl_miner::get_num_devices(unsigned _platformId)
return 0; return 0;
} }
std::vector<cl::Device> devices; vector<cl::Device> devices;
unsigned platform_num = std::min<unsigned>(_platformId, platforms.size() - 1); unsigned platform_num = min<unsigned>(_platformId, platforms.size() - 1);
platforms[platform_num].getDevices(CL_DEVICE_TYPE_ALL, &devices); platforms[platform_num].getDevices(CL_DEVICE_TYPE_ALL, &devices);
if (devices.empty()) if (devices.empty())
{ {
@ -127,9 +127,34 @@ unsigned ethash_cl_miner::get_num_devices(unsigned _platformId)
return devices.size(); 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); cl::Platform::get(&platforms);
if (platforms.empty()) if (platforms.empty())
{ {
@ -137,50 +162,31 @@ bool ethash_cl_miner::haveSufficientGPUMemory()
return false; return false;
} }
for (unsigned i = 0; i < platforms.size(); ++i) for (unsigned i = 0; i < platforms.size(); ++i)
if (haveSufficientGPUMemory(i)) if (searchForAllDevices(i, _callback))
return true; return true;
return false; 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); cl::Platform::get(&platforms);
if (_platformId >= platforms.size()) if (_platformId >= platforms.size())
return false; return false;
std::vector<cl::Device> devices; vector<cl::Device> devices;
unsigned platform_num = std::min<unsigned>(_platformId, platforms.size() - 1); platforms[_platformId].getDevices(CL_DEVICE_TYPE_ALL, &devices);
platforms[platform_num].getDevices(CL_DEVICE_TYPE_ALL, &devices);
if (devices.empty())
return false;
for (cl::Device const& device: devices) for (cl::Device const& device: devices)
{ if (_callback(device))
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; 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; 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); cl::Platform::get(&platforms);
if (platforms.empty()) if (platforms.empty())
{ {
@ -188,26 +194,32 @@ void ethash_cl_miner::listDevices()
return; return;
} }
for (unsigned i = 0; i < platforms.size(); ++i) 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); cl::Platform::get(&platforms);
if (_platformId >= platforms.size()) if (_platformId >= platforms.size())
return; return;
std::string outString ="Listing OpenCL devices for platform " + to_string(_platformId) + "\n[deviceID] deviceName\n"; vector<cl::Device> devices;
std::vector<cl::Device> devices;
platforms[_platformId].getDevices(CL_DEVICE_TYPE_ALL, &devices); platforms[_platformId].getDevices(CL_DEVICE_TYPE_ALL, &devices);
unsigned i = 0;
std::string deviceString;
for (cl::Device const& device: devices) for (cl::Device const& device: devices)
{ _callback(device);
outString += "[" + to_string(i) + "] " + device.getInfo<CL_DEVICE_NAME>() + "\n"; }
++i;
} 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); ETHCL_LOG(outString);
} }
@ -222,19 +234,13 @@ bool ethash_cl_miner::init(
uint64_t _dagSize, uint64_t _dagSize,
unsigned workgroup_size, unsigned workgroup_size,
unsigned _platformId, unsigned _platformId,
unsigned _deviceId, unsigned _deviceId
unsigned _dagChunksNum
) )
{ {
// 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 // get all platforms
try try
{ {
std::vector<cl::Platform> platforms; vector<cl::Platform> platforms;
cl::Platform::get(&platforms); cl::Platform::get(&platforms);
if (platforms.empty()) if (platforms.empty())
{ {
@ -243,11 +249,11 @@ bool ethash_cl_miner::init(
} }
// use selected platform // 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()); ETHCL_LOG("Using platform: " << platforms[_platformId].getInfo<CL_PLATFORM_NAME>().c_str());
// get GPU device of the default platform // get GPU device of the default platform
std::vector<cl::Device> devices; vector<cl::Device> devices;
platforms[_platformId].getDevices(CL_DEVICE_TYPE_ALL, &devices); platforms[_platformId].getDevices(CL_DEVICE_TYPE_ALL, &devices);
if (devices.empty()) if (devices.empty())
{ {
@ -256,10 +262,14 @@ bool ethash_cl_miner::init(
} }
// use selected device // use selected device
cl::Device& device = devices[std::min<unsigned>(_deviceId, devices.size() - 1)]; cl::Device& device = devices[min<unsigned>(_deviceId, devices.size() - 1)];
std::string device_version = device.getInfo<CL_DEVICE_VERSION>(); string device_version = device.getInfo<CL_DEVICE_VERSION>();
ETHCL_LOG("Using device: " << device.getInfo<CL_DEVICE_NAME>().c_str() << "(" << device_version.c_str() << ")"); 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);
m_dagChunksNum = result >= ETHASH_CL_MINIMUM_MEMORY ? 4 : 1;
if (strncmp("OpenCL 1.0", device_version.c_str(), 10) == 0) if (strncmp("OpenCL 1.0", device_version.c_str(), 10) == 0)
{ {
ETHCL_LOG("OpenCL 1.0 is not supported."); ETHCL_LOG("OpenCL 1.0 is not supported.");
@ -269,7 +279,7 @@ bool ethash_cl_miner::init(
m_opencl_1_1 = true; m_opencl_1_1 = true;
// create context // 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); m_queue = cl::CommandQueue(m_context, device);
// use requested workgroup size, but we require multiple of 8 // use requested workgroup size, but we require multiple of 8
@ -278,11 +288,11 @@ bool ethash_cl_miner::init(
// patch source code // patch source code
// note: ETHASH_CL_MINER_KERNEL is simply ethash_cl_miner_kernel.cl compiled // 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 // 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); string code(ETHASH_CL_MINER_KERNEL, ETHASH_CL_MINER_KERNEL + ETHASH_CL_MINER_KERNEL_SIZE);
add_definition(code, "GROUP_SIZE", m_workgroup_size); addDefinition(code, "GROUP_SIZE", m_workgroup_size);
add_definition(code, "DAG_SIZE", (unsigned)(_dagSize / ETHASH_MIX_BYTES)); addDefinition(code, "DAG_SIZE", (unsigned)(_dagSize / ETHASH_MIX_BYTES));
add_definition(code, "ACCESSES", ETHASH_ACCESSES); addDefinition(code, "ACCESSES", ETHASH_ACCESSES);
add_definition(code, "MAX_OUTPUTS", c_max_search_results); addDefinition(code, "MAX_OUTPUTS", c_max_search_results);
//debugf("%s", code.c_str()); //debugf("%s", code.c_str());
// create miner OpenCL program // create miner OpenCL program
@ -301,7 +311,7 @@ bool ethash_cl_miner::init(
ETHCL_LOG(program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(device).c_str()); ETHCL_LOG(program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(device).c_str());
return false; return false;
} }
if (_dagChunksNum == 1) if (m_dagChunksNum == 1)
{ {
ETHCL_LOG("Loading single big chunk kernels"); ETHCL_LOG("Loading single big chunk kernels");
m_hash_kernel = cl::Kernel(program, "ethash_hash"); m_hash_kernel = cl::Kernel(program, "ethash_hash");
@ -315,13 +325,13 @@ bool ethash_cl_miner::init(
} }
// create buffer for dag // create buffer for dag
if (_dagChunksNum == 1) if (m_dagChunksNum == 1)
{ {
ETHCL_LOG("Creating one big buffer"); ETHCL_LOG("Creating one big buffer");
m_dagChunks.push_back(cl::Buffer(m_context, CL_MEM_READ_ONLY, _dagSize)); m_dagChunks.push_back(cl::Buffer(m_context, CL_MEM_READ_ONLY, _dagSize));
} }
else 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 // TODO Note: If we ever change to _dagChunksNum other than 4, then the size would need recalculation
ETHCL_LOG("Creating buffer for chunk " << i); ETHCL_LOG("Creating buffer for chunk " << i);
@ -336,7 +346,7 @@ bool ethash_cl_miner::init(
ETHCL_LOG("Creating buffer for header."); ETHCL_LOG("Creating buffer for header.");
m_header = cl::Buffer(m_context, CL_MEM_READ_ONLY, 32); m_header = cl::Buffer(m_context, CL_MEM_READ_ONLY, 32);
if (_dagChunksNum == 1) if (m_dagChunksNum == 1)
{ {
ETHCL_LOG("Mapping one big chunk."); ETHCL_LOG("Mapping one big chunk.");
m_queue.enqueueWriteBuffer(m_dagChunks[0], CL_TRUE, 0, _dagSize, _dag); m_queue.enqueueWriteBuffer(m_dagChunks[0], CL_TRUE, 0, _dagSize, _dag);
@ -345,12 +355,12 @@ bool ethash_cl_miner::init(
{ {
// TODO Note: If we ever change to _dagChunksNum other than 4, then the size would need recalculation // TODO Note: If we ever change to _dagChunksNum other than 4, then the size would need recalculation
void* dag_ptr[4]; void* dag_ptr[4];
for (unsigned i = 0; i < _dagChunksNum; i++) for (unsigned i = 0; i < m_dagChunksNum; i++)
{ {
ETHCL_LOG("Mapping chunk " << 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); 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); 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]); m_queue.enqueueUnmapMemObject(m_dagChunks[i], dag_ptr[i]);
@ -382,7 +392,7 @@ void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook
uint64_t start_nonce; uint64_t start_nonce;
unsigned buf; unsigned buf;
}; };
std::queue<pending_batch> pending; queue<pending_batch> pending;
static uint32_t const c_zero = 0; static uint32_t const c_zero = 0;
@ -408,8 +418,8 @@ void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook
m_search_kernel.setArg(argPos + 2, ~0u); m_search_kernel.setArg(argPos + 2, ~0u);
unsigned buf = 0; unsigned buf = 0;
std::random_device engine; random_device engine;
uint64_t start_nonce = std::uniform_int_distribution<uint64_t>()(engine); uint64_t start_nonce = uniform_int_distribution<uint64_t>()(engine);
for (;; start_nonce += c_search_batch_size) for (;; start_nonce += c_search_batch_size)
{ {
// supply output buffer to kernel // supply output buffer to kernel
@ -432,7 +442,7 @@ void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook
// could use pinned host pointer instead // 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)); 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]; uint64_t nonces[c_max_search_results];
for (unsigned i = 0; i != num_found; ++i) 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();
~ethash_cl_miner(); ~ethash_cl_miner();
static unsigned get_num_platforms(); static bool searchForAllDevices(unsigned _platformId, std::function<bool(cl::Device const&)> _callback);
static unsigned get_num_devices(unsigned _platformId = 0); 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 std::string platform_info(unsigned _platformId = 0, unsigned _deviceId = 0);
static bool haveSufficientGPUMemory();
static bool haveSufficientGPUMemory(unsigned _platformId);
static void listDevices(); static void listDevices();
static void listDevices(unsigned _platformId); static bool configureGPU();
bool init( bool init(
uint8_t const* _dag, uint8_t const* _dag,
uint64_t _dagSize, uint64_t _dagSize,
unsigned workgroup_size = 64, unsigned workgroup_size = 64,
unsigned _platformId = 0, unsigned _platformId = 0,
unsigned _deviceId = 0, unsigned _deviceId = 0
unsigned _dagChunksNum = 1
); );
void finish(); void finish();
void search(uint8_t const* header, uint64_t target, search_hook& hook); void search(uint8_t const* header, uint64_t target, search_hook& hook);
@ -61,11 +62,12 @@ private:
cl::CommandQueue m_queue; cl::CommandQueue m_queue;
cl::Kernel m_hash_kernel; cl::Kernel m_hash_kernel;
cl::Kernel m_search_kernel; cl::Kernel m_search_kernel;
unsigned m_dagChunksNum; unsigned int m_dagChunksNum;
std::vector<cl::Buffer> m_dagChunks; std::vector<cl::Buffer> m_dagChunks;
cl::Buffer m_header; cl::Buffer m_header;
cl::Buffer m_hash_buf[c_num_buffers]; cl::Buffer m_hash_buf[c_num_buffers];
cl::Buffer m_search_buf[c_num_buffers]; cl::Buffer m_search_buf[c_num_buffers];
unsigned m_workgroup_size; unsigned m_workgroup_size;
bool m_opencl_1_1; 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 LatestBlock = (BlockNumber)-2;
static const BlockNumber PendingBlock = (BlockNumber)-1; 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 enum class RelativeBlock: BlockNumber
{ {

9
libethcore/Ethash.cpp

@ -285,7 +285,6 @@ private:
unsigned Ethash::GPUMiner::s_platformId = 0; unsigned Ethash::GPUMiner::s_platformId = 0;
unsigned Ethash::GPUMiner::s_deviceId = 0; unsigned Ethash::GPUMiner::s_deviceId = 0;
unsigned Ethash::GPUMiner::s_numInstances = 0; unsigned Ethash::GPUMiner::s_numInstances = 0;
unsigned Ethash::GPUMiner::s_dagChunks = 1;
Ethash::GPUMiner::GPUMiner(ConstructionInfo const& _ci): Ethash::GPUMiner::GPUMiner(ConstructionInfo const& _ci):
Miner(_ci), Miner(_ci),
@ -347,7 +346,7 @@ void Ethash::GPUMiner::workLoop()
this_thread::sleep_for(chrono::milliseconds(500)); this_thread::sleep_for(chrono::milliseconds(500));
} }
bytesConstRef dagData = dag->data(); 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); uint64_t upper64OfBoundary = (uint64_t)(u64)((u256)w.boundary >> 192);
@ -374,7 +373,7 @@ std::string Ethash::GPUMiner::platformInfo()
unsigned Ethash::GPUMiner::getNumDevices() unsigned Ethash::GPUMiner::getNumDevices()
{ {
return ethash_cl_miner::get_num_devices(s_platformId); return ethash_cl_miner::getNumDevices(s_platformId);
} }
void Ethash::GPUMiner::listDevices() void Ethash::GPUMiner::listDevices()
@ -382,9 +381,9 @@ void Ethash::GPUMiner::listDevices()
return ethash_cl_miner::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 #endif

6
libethcore/Ethash.h

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

4
libethereum/BlockChain.cpp

@ -926,8 +926,8 @@ void BlockChain::checkConsistency()
delete it; delete it;
} }
static inline unsigned upow(unsigned a, unsigned b) { while (b-- > 0) a *= a; return a; } 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 / (n + d - 1); } 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 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)); } //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()); } BlockLogBlooms logBlooms() const { return logBlooms(currentHash()); }
/// Get the transactions' receipts of a block (or the most recent mined if none given). Thread-safe. /// 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(h256 const& _hash) const { return queryExtras<BlockReceipts, ExtraReceipts>(_hash, m_receipts, x_receipts, NullBlockReceipts); }
BlockReceipts receipts() const { return receipts(currentHash()); } BlockReceipts receipts() const { return receipts(currentHash()); }

183
libethereum/BlockQueue.cpp

@ -37,8 +37,16 @@ const char* BlockQueueChannel::name() { return EthOrange "[]>"; }
const char* BlockQueueChannel::name() { return EthOrange "▣┅▶"; } const char* BlockQueueChannel::name() { return EthOrange "▣┅▶"; }
#endif #endif
size_t const c_maxKnownCount = 100000;
BlockQueue::BlockQueue() 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 // Allow some room for other activity
unsigned verifierThreads = std::max(thread::hardware_concurrency(), 3U) - 2U; unsigned verifierThreads = std::max(thread::hardware_concurrency(), 3U) - 2U;
@ -57,11 +65,29 @@ BlockQueue::~BlockQueue()
i.join(); 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() void BlockQueue::verifierBody()
{ {
while (!m_deleting) while (!m_deleting)
{ {
std::pair<h256, bytes> work; UnverifiedBlock work;
{ {
unique_lock<Mutex> l(m_verification); unique_lock<Mutex> l(m_verification);
@ -71,12 +97,13 @@ void BlockQueue::verifierBody()
swap(work, m_unverified.front()); swap(work, m_unverified.front());
m_unverified.pop_front(); m_unverified.pop_front();
BlockInfo bi; 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() }); m_verifying.push_back(VerifiedBlock { VerifiedBlockRef { bytesConstRef(), move(bi), Transactions() }, bytes() });
} }
VerifiedBlock res; VerifiedBlock res;
swap(work.second, res.blockData); swap(work.block, res.blockData);
try try
{ {
res.verified = BlockChain::verifyBlock(res.blockData, m_onBad); res.verified = BlockChain::verifyBlock(res.blockData, m_onBad);
@ -88,13 +115,13 @@ void BlockQueue::verifierBody()
// has to be this order as that's how invariants() assumes. // has to be this order as that's how invariants() assumes.
WriteGuard l2(m_lock); WriteGuard l2(m_lock);
unique_lock<Mutex> l(m_verification); unique_lock<Mutex> l(m_verification);
m_readySet.erase(work.first); m_readySet.erase(work.hash);
m_knownBad.insert(work.first); m_knownBad.insert(work.hash);
} }
unique_lock<Mutex> l(m_verification); unique_lock<Mutex> l(m_verification);
for (auto it = m_verifying.begin(); it != m_verifying.end(); ++it) 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); m_verifying.erase(it);
goto OK1; goto OK1;
@ -106,12 +133,13 @@ void BlockQueue::verifierBody()
bool ready = false; bool ready = false;
{ {
WriteGuard l2(m_lock);
unique_lock<Mutex> l(m_verification); unique_lock<Mutex> l(m_verification);
if (!m_verifying.empty() && m_verifying.front().verified.info.mixHash == work.first) if (!m_verifying.empty() && m_verifying.front().verified.info.mixHash == work.hash)
{ {
// we're next! // we're next!
m_verifying.pop_front(); m_verifying.pop_front();
if (m_knownBad.count(res.verified.info.hash())) if (m_knownBad.count(res.verified.info.parentHash))
{ {
m_readySet.erase(res.verified.info.hash()); m_readySet.erase(res.verified.info.hash());
m_knownBad.insert(res.verified.info.hash()); m_knownBad.insert(res.verified.info.hash());
@ -120,7 +148,7 @@ void BlockQueue::verifierBody()
m_verified.push_back(move(res)); m_verified.push_back(move(res));
while (m_verifying.size() && !m_verifying.front().blockData.empty()) while (m_verifying.size() && !m_verifying.front().blockData.empty())
{ {
if (m_knownBad.count(m_verifying.front().verified.info.hash())) if (m_knownBad.count(m_verifying.front().verified.info.parentHash))
{ {
m_readySet.erase(m_verifying.front().verified.info.hash()); m_readySet.erase(m_verifying.front().verified.info.hash());
m_knownBad.insert(res.verified.info.hash()); m_knownBad.insert(res.verified.info.hash());
@ -134,7 +162,7 @@ void BlockQueue::verifierBody()
else else
{ {
for (auto& i: m_verifying) for (auto& i: m_verifying)
if (i.verified.info.mixHash == work.first) if (i.verified.info.mixHash == work.hash)
{ {
i = move(res); i = move(res);
goto OK; goto OK;
@ -199,6 +227,8 @@ ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc, boo
if (strftime(buf, 24, "%X", localtime(&bit)) == 0) if (strftime(buf, 24, "%X", localtime(&bit)) == 0)
buf[0] = '\0'; // empty if case strftime fails buf[0] = '\0'; // empty if case strftime fails
cblockq << "OK - queued for future [" << bi.timestamp << "vs" << time(0) << "] - will wait until" << buf; cblockq << "OK - queued for future [" << bi.timestamp << "vs" << time(0) << "] - will wait until" << buf;
m_unknownSize += _block.size();
m_unknownCount++;
return ImportResult::FutureTime; return ImportResult::FutureTime;
} }
else else
@ -207,6 +237,7 @@ ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc, boo
if (m_knownBad.count(bi.parentHash)) if (m_knownBad.count(bi.parentHash))
{ {
m_knownBad.insert(bi.hash()); m_knownBad.insert(bi.hash());
updateBad(bi.hash());
// bad parent; this is bad too, note it as such // bad parent; this is bad too, note it as such
return ImportResult::BadChain; return ImportResult::BadChain;
} }
@ -216,6 +247,8 @@ ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc, boo
cblockq << "OK - queued as unknown parent:" << bi.parentHash; cblockq << "OK - queued as unknown parent:" << bi.parentHash;
m_unknown.insert(make_pair(bi.parentHash, make_pair(h, _block.toBytes()))); m_unknown.insert(make_pair(bi.parentHash, make_pair(h, _block.toBytes())));
m_unknownSet.insert(h); m_unknownSet.insert(h);
m_unknownSize += _block.size();
m_unknownCount++;
return ImportResult::UnknownParent; return ImportResult::UnknownParent;
} }
@ -224,9 +257,11 @@ ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc, boo
// If valid, append to blocks. // If valid, append to blocks.
cblockq << "OK - ready for chain insertion."; cblockq << "OK - ready for chain insertion.";
DEV_GUARDED(m_verification) 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_moreToVerify.notify_one();
m_readySet.insert(h); m_readySet.insert(h);
m_knownSize += _block.size();
m_knownCount++;
noteReady_WITH_LOCK(h); noteReady_WITH_LOCK(h);
@ -235,39 +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; DEV_INVARIANT_CHECK;
m_drainingSet.clear(); DEV_GUARDED(m_verification)
if (_bad.size())
{ {
// at least one of them was bad. collectUnknownBad(_bad);
m_knownBad += _bad; bool moreBad = true;
DEV_GUARDED(m_verification) while (moreBad)
{ {
moreBad = false;
std::vector<VerifiedBlock> oldVerified; std::vector<VerifiedBlock> oldVerified;
swap(m_verified, oldVerified); swap(m_verified, oldVerified);
for (auto& b: oldVerified) for (auto& b: oldVerified)
if (m_knownBad.count(b.verified.info.parentHash)) if (m_knownBad.count(b.verified.info.parentHash) || m_knownBad.count(b.verified.info.hash()))
{ {
m_knownBad.insert(b.verified.info.hash()); m_knownBad.insert(b.verified.info.hash());
m_readySet.erase(b.verified.info.hash()); m_readySet.erase(b.verified.info.hash());
collectUnknownBad(b.verified.info.hash());
moreBad = true;
} }
else else
m_verified.push_back(std::move(b)); 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));
} }
} }
/* DEV_GUARDED(m_verification) 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_knownBad += _bad; m_unknownSize -= it->second.second.size();
m_knownBad += m_readySet; m_unknownCount--;
m_readySet.clear(); auto newBad = it->second.first;
m_verified.clear(); m_unknownSet.erase(newBad);
m_verifying.clear(); m_knownBad.insert(newBad);
m_unverified.clear(); badQueue.push_back(newBad);
}*/ }
return !m_readySet.empty(); 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) void BlockQueue::tick(BlockChain const& _bc)
@ -291,7 +380,11 @@ void BlockQueue::tick(BlockChain const& _bc)
DEV_INVARIANT_CHECK; DEV_INVARIANT_CHECK;
auto end = m_future.lower_bound(t); auto end = m_future.lower_bound(t);
for (auto i = m_future.begin(); i != end; ++i) for (auto i = m_future.begin(); i != end; ++i)
{
m_unknownSize -= i->second.second.size();
m_unknownCount--;
todo.push_back(move(i->second)); todo.push_back(move(i->second));
}
m_future.erase(m_future.begin(), end); m_future.erase(m_future.begin(), end);
} }
} }
@ -322,12 +415,24 @@ QueueStatus BlockQueue::blockStatus(h256 const& _h) const
QueueStatus::Unknown; 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) void BlockQueue::drain(VerifiedBlocks& o_out, unsigned _max)
{ {
WriteGuard l(m_lock); WriteGuard l(m_lock);
DEV_INVARIANT_CHECK; DEV_INVARIANT_CHECK;
if (m_drainingSet.empty()) if (m_drainingSet.empty())
{ {
bool wasFull = knownFull();
DEV_GUARDED(m_verification) DEV_GUARDED(m_verification)
{ {
o_out.resize(min<unsigned>(_max, m_verified.size())); o_out.resize(min<unsigned>(_max, m_verified.size()));
@ -341,8 +446,13 @@ void BlockQueue::drain(VerifiedBlocks& o_out, unsigned _max)
auto h = bs.verified.info.hash(); auto h = bs.verified.info.hash();
m_drainingSet.insert(h); m_drainingSet.insert(h);
m_readySet.erase(h); m_readySet.erase(h);
m_knownSize -= bs.verified.block.size();
m_knownCount--;
} }
if (wasFull && !knownFull())
m_onRoomAvailable();
} }
} }
bool BlockQueue::invariants() const bool BlockQueue::invariants() const
@ -363,7 +473,11 @@ void BlockQueue::noteReady_WITH_LOCK(h256 const& _good)
for (auto it = r.first; it != r.second; ++it) for (auto it = r.first; it != r.second; ++it)
{ {
DEV_GUARDED(m_verification) 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; auto newReady = it->second.first;
m_unknownSet.erase(newReady); m_unknownSet.erase(newReady);
m_readySet.insert(newReady); m_readySet.insert(newReady);
@ -374,6 +488,7 @@ void BlockQueue::noteReady_WITH_LOCK(h256 const& _good)
} }
if (notify) if (notify)
m_moreToVerify.notify_all(); m_moreToVerify.notify_all();
DEV_INVARIANT_CHECK;
} }
void BlockQueue::retryAllUnknown() void BlockQueue::retryAllUnknown()
@ -383,13 +498,17 @@ void BlockQueue::retryAllUnknown()
for (auto it = m_unknown.begin(); it != m_unknown.end(); ++it) for (auto it = m_unknown.begin(); it != m_unknown.end(); ++it)
{ {
DEV_GUARDED(m_verification) 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; auto newReady = it->second.first;
m_unknownSet.erase(newReady); m_unknownSet.erase(newReady);
m_readySet.insert(newReady); m_readySet.insert(newReady);
m_knownCount++;
m_moreToVerify.notify_one(); m_moreToVerify.notify_one();
} }
m_unknown.clear(); m_unknown.clear();
m_knownSize += m_unknownSize;
m_unknownSize = 0;
m_unknownCount = 0;
m_moreToVerify.notify_all(); m_moreToVerify.notify_all();
} }

28
libethereum/BlockQueue.h

@ -76,7 +76,7 @@ public:
~BlockQueue(); ~BlockQueue();
/// Import a block into the queue. /// 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. /// 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); 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()); } std::pair<unsigned, unsigned> items() const { ReadGuard l(m_lock); return std::make_pair(m_readySet.size(), m_unknownSet.size()); }
/// Clear everything. /// 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. /// Return first block with an unknown parent.
h256 firstUnknown() const { ReadGuard l(m_lock); return m_unknownSet.size() ? *m_unknownSet.begin() : h256(); } 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; QueueStatus blockStatus(h256 const& _h) const;
template <class T> Handler onReady(T const& _t) { return m_onReady.add(_t); } 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; } template <class T> void setOnBad(T const& _t) { m_onBad = _t; }
bool knownFull() const;
bool unknownFull() const;
private: private:
struct UnverifiedBlock
{
h256 hash;
h256 parentHash;
bytes block;
};
void noteReady_WITH_LOCK(h256 const& _b); void noteReady_WITH_LOCK(h256 const& _b);
bool invariants() const override; bool invariants() const override;
void verifierBody(); 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. mutable boost::shared_mutex m_lock; ///< General lock for the sets, m_future and m_unknown.
h256Hash m_drainingSet; ///< All blocks being imported. 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. 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 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_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. 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::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::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<VerifiedBlock> m_verifying; ///< List of blocks being verified; as long as the block 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::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. std::vector<std::thread> m_verifiers; ///< Threads who only verify.
bool m_deleting = false; ///< Exit condition for verifiers. 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); std::ostream& operator<<(std::ostream& _out, BlockQueueStatus const& _s);

78
libethereum/Client.cpp

@ -428,21 +428,17 @@ void Client::killChain()
void Client::clearPending() void Client::clearPending()
{ {
h256Hash changeds;
DEV_WRITE_GUARDED(x_postMine) DEV_WRITE_GUARDED(x_postMine)
{ {
if (!m_postMine.pending().size()) if (!m_postMine.pending().size())
return; return;
// for (unsigned i = 0; i < m_postMine.pending().size(); ++i)
// appendFromNewPending(m_postMine.logBloom(i), changeds);
changeds.insert(PendingChangedFilter);
m_tq.clear(); m_tq.clear();
DEV_READ_GUARDED(x_preMine) DEV_READ_GUARDED(x_preMine)
m_postMine = m_preMine; m_postMine = m_preMine;
} }
startMining(); startMining();
h256Hash changeds;
noteChanged(changeds); noteChanged(changeds);
} }
@ -465,47 +461,53 @@ static S& filtersStreamOut(S& _out, T const& _fs)
return _out; 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); Guard l(x_filtersWatches);
io_changed.insert(PendingChangedFilter);
m_specialFilters.at(PendingChangedFilter).push_back(_sha3);
for (pair<h256 const, InstalledFilter>& i: m_filters) 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. // filter catches them
auto m = i.second.filter.matches(_receipt); for (LogEntry const& l: m)
if (m.size()) i.second.changes.push_back(LocalisedLogEntry(l));
{ io_changed.insert(i.first);
// 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);
}
} }
}
} }
void Client::appendFromNewBlock(h256 const& _block, h256Hash& io_changed) void Client::appendFromNewBlock(h256 const& _block, h256Hash& io_changed)
{ {
// TODO: more precise check on whether the txs match. // TODO: more precise check on whether the txs match.
auto d = m_bc.info(_block); auto d = m_bc.info(_block);
auto br = m_bc.receipts(_block); auto receipts = m_bc.receipts(_block).receipts;
Guard l(x_filtersWatches); Guard l(x_filtersWatches);
io_changed.insert(ChainChangedFilter);
m_specialFilters.at(ChainChangedFilter).push_back(_block);
for (pair<h256 const, InstalledFilter>& i: m_filters) 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. // acceptable number & looks like block may contain a matching log entry.
for (size_t j = 0; j < br.receipts.size(); j++) 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 transactionHash = transaction(d.hash(), j).sha3();
auto m = i.second.filter.matches(tr); // filter catches them
if (m.size()) for (LogEntry const& l: m)
{ i.second.changes.push_back(LocalisedLogEntry(l, d, transactionHash, j, logIndex));
auto transactionHash = transaction(d.hash(), j).sha3(); io_changed.insert(i.first);
// filter catches them
for (LogEntry const& l: m)
i.second.changes.push_back(LocalisedLogEntry(l, (unsigned)d.number, transactionHash));
io_changed.insert(i.first);
}
} }
}
}
} }
void Client::setForceMining(bool _enable) void Client::setForceMining(bool _enable)
@ -643,7 +645,7 @@ void Client::syncTransactionQueue()
DEV_READ_GUARDED(x_postMine) DEV_READ_GUARDED(x_postMine)
for (size_t i = 0; i < newPendingReceipts.size(); i++) for (size_t i = 0; i < newPendingReceipts.size(); i++)
appendFromNewPending(newPendingReceipts[i], changeds, m_postMine.pending()[i].sha3()); appendFromNewPending(newPendingReceipts[i], changeds, m_postMine.pending()[i].sha3());
changeds.insert(PendingChangedFilter);
// Tell farm about new transaction (i.e. restartProofOfWork mining). // Tell farm about new transaction (i.e. restartProofOfWork mining).
onPostStateChanged(); onPostStateChanged();
@ -686,7 +688,6 @@ void Client::onChainChanged(ImportRoute const& _ir)
h256Hash changeds; h256Hash changeds;
for (auto const& h: _ir.first) for (auto const& h: _ir.first)
appendFromNewBlock(h, changeds); appendFromNewBlock(h, changeds);
changeds.insert(ChainChangedFilter);
// RESTART MINING // RESTART MINING
@ -792,15 +793,18 @@ void Client::noteChanged(h256Hash const& _filters)
cwatch << "!!!" << w.first << w.second.id.abridged(); cwatch << "!!!" << w.first << w.second.id.abridged();
w.second.changes += m_filters.at(w.second.id).changes; w.second.changes += m_filters.at(w.second.id).changes;
} }
else 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, 0)); 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. // clear the filters now.
for (auto& i: m_filters) for (auto& i: m_filters)
i.second.changes.clear(); i.second.changes.clear();
for (auto& i: m_specialFilters)
i.second.clear();
} }
void Client::doWork() void Client::doWork()

33
libethereum/ClientBase.cpp

@ -171,8 +171,8 @@ LocalisedLogEntries ClientBase::logs(unsigned _watchId) const
LocalisedLogEntries ClientBase::logs(LogFilter const& _f) const LocalisedLogEntries ClientBase::logs(LogFilter const& _f) const
{ {
LocalisedLogEntries ret; LocalisedLogEntries ret;
unsigned begin = min<unsigned>(bc().number() + 1, (unsigned)_f.latest()); unsigned begin = min(bc().number() + 1, (unsigned)numberFromHash(_f.latest()));
unsigned end = min(bc().number(), min(begin, (unsigned)_f.earliest())); unsigned end = min(bc().number(), min(begin, (unsigned)numberFromHash(_f.earliest())));
// Handle pending transactions differently as they're not on the block chain. // Handle pending transactions differently as they're not on the block chain.
if (begin > bc().number()) if (begin > bc().number())
@ -182,11 +182,10 @@ LocalisedLogEntries ClientBase::logs(LogFilter const& _f) const
{ {
// Might have a transaction that contains a matching log. // Might have a transaction that contains a matching log.
TransactionReceipt const& tr = temp.receipt(i); TransactionReceipt const& tr = temp.receipt(i);
auto th = temp.pending()[i].sha3();
LogEntries le = _f.matches(tr); LogEntries le = _f.matches(tr);
if (le.size()) if (le.size())
for (unsigned j = 0; j < le.size(); ++j) 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(); begin = bc().number();
} }
@ -201,20 +200,22 @@ LocalisedLogEntries ClientBase::logs(LogFilter const& _f) const
{ {
int total = 0; int total = 0;
auto h = bc().numberHash(n); auto h = bc().numberHash(n);
auto info = bc().info(h);
auto receipts = bc().receipts(h).receipts; auto receipts = bc().receipts(h).receipts;
unsigned logIndex = 0;
for (size_t i = 0; i < receipts.size(); i++) for (size_t i = 0; i < receipts.size(); i++)
{ {
logIndex++;
TransactionReceipt receipt = receipts[i]; TransactionReceipt receipt = receipts[i];
if (_f.matches(receipt.bloom())) if (_f.matches(receipt.bloom()))
{ {
auto info = bc().info(h);
auto th = transaction(info.hash(), i).sha3(); auto th = transaction(info.hash(), i).sha3();
LogEntries le = _f.matches(receipt); LogEntries le = _f.matches(receipt);
if (le.size()) if (le.size())
{ {
total += le.size(); total += le.size();
for (unsigned j = 0; j < le.size(); ++j) 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 BlockInfo ClientBase::blockInfo(h256 _hash) const
{ {
if (_hash == PendingBlockHash)
return preMine().info();
return BlockInfo(bc().block(_hash)); return BlockInfo(bc().block(_hash));
} }
@ -441,6 +444,24 @@ h256 ClientBase::hashFromNumber(BlockNumber _number) const
BlockNumber ClientBase::numberFromHash(h256 _blockHash) 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); 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 h256 ChainChangedFilter = u256(1);
static const LogEntry SpecialLogEntry = LogEntry(Address(), h256s(), bytes()); static const LogEntry SpecialLogEntry = LogEntry(Address(), h256s(), bytes());
static const LocalisedLogEntry InitialChange(SpecialLogEntry, 0); static const LocalisedLogEntry InitialChange(SpecialLogEntry);
struct ClientWatch struct ClientWatch
{ {
@ -117,6 +117,7 @@ public:
virtual h256 hashFromNumber(BlockNumber _number) const override; virtual h256 hashFromNumber(BlockNumber _number) const override;
virtual BlockNumber numberFromHash(h256 _blockHash) 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 BlockInfo blockInfo(h256 _hash) const override;
virtual BlockDetails blockDetails(h256 _hash) const override; virtual BlockDetails blockDetails(h256 _hash) const override;
virtual Transaction transaction(h256 _transactionHash) const override; virtual Transaction transaction(h256 _transactionHash) const override;
@ -175,6 +176,8 @@ protected:
// filters // filters
mutable Mutex x_filtersWatches; ///< Our lock. mutable Mutex x_filtersWatches; ///< Our lock.
std::unordered_map<h256, InstalledFilter> m_filters; ///< The dictionary of filters that are active. 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. std::map<unsigned, ClientWatch> m_watches; ///< Each and every watch - these reference a filter.
}; };

70
libethereum/EthereumHost.cpp

@ -39,6 +39,7 @@ using namespace dev::eth;
using namespace p2p; using namespace p2p;
unsigned const EthereumHost::c_oldProtocolVersion = 60; //TODO: remove this once v61+ is common 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): EthereumHost::EthereumHost(BlockChain const& _ch, TransactionQueue& _tq, BlockQueue& _bq, u256 _networkId):
HostCapability<EthereumPeer>(), HostCapability<EthereumPeer>(),
@ -48,9 +49,9 @@ EthereumHost::EthereumHost(BlockChain const& _ch, TransactionQueue& _tq, BlockQu
m_bq (_bq), m_bq (_bq),
m_networkId (_networkId) m_networkId (_networkId)
{ {
m_bq.onReady([=](){ if (readyForMore()) m_continueSync = true; });
m_latestBlockSent = _ch.currentHash(); m_latestBlockSent = _ch.currentHash();
m_hashMan.reset(m_chain.number() + 1); m_hashMan.reset(m_chain.number() + 1);
m_bqRoomAvailable = m_bq.onRoomAvailable([this](){ m_continueSync = true; });
} }
EthereumHost::~EthereumHost() EthereumHost::~EthereumHost()
@ -257,37 +258,43 @@ void EthereumHost::onPeerStatus(EthereumPeer* _peer)
_peer->disable("Peer banned for previous bad behaviour."); _peer->disable("Peer banned for previous bad behaviour.");
else else
{ {
if (_peer->m_protocolVersion != protocolVersion()) unsigned estimatedHashes = estimateHashes();
estimatePeerHashes(_peer); if (_peer->m_protocolVersion == protocolVersion())
else
{ {
if (_peer->m_latestBlockNumber > m_chain.number()) if (_peer->m_latestBlockNumber > m_chain.number())
_peer->m_expectedHashes = (unsigned)_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); m_hashMan.resetToRange(m_chain.number() + 1, _peer->m_expectedHashes);
} }
else
_peer->m_expectedHashes = estimatedHashes;
continueSync(_peer); continueSync(_peer);
} }
} }
void EthereumHost::estimatePeerHashes(EthereumPeer* _peer) unsigned EthereumHost::estimateHashes()
{ {
BlockInfo block = m_chain.info(); BlockInfo block = m_chain.info();
time_t lastBlockTime = (block.hash() == m_chain.genesisHash()) ? 1428192000 : (time_t)block.timestamp; time_t lastBlockTime = (block.hash() == m_chain.genesisHash()) ? 1428192000 : (time_t)block.timestamp;
time_t now = time(0); time_t now = time(0);
unsigned blockCount = 30000; unsigned blockCount = c_chainReorgSize;
if (lastBlockTime > now) if (lastBlockTime > now)
clog(NetWarn) << "Clock skew? Latest block is in the future"; clog(NetWarn) << "Clock skew? Latest block is in the future";
else else
blockCount += (now - lastBlockTime) / (unsigned)c_durationLimit; blockCount += (now - lastBlockTime) / (unsigned)c_durationLimit;
clog(NetAllDetail) << "Estimated hashes: " << blockCount; clog(NetAllDetail) << "Estimated hashes: " << blockCount;
_peer->m_expectedHashes = blockCount; return blockCount;
} }
void EthereumHost::onPeerHashes(EthereumPeer* _peer, h256s const& _hashes) void EthereumHost::onPeerHashes(EthereumPeer* _peer, h256s const& _hashes)
{ {
RecursiveGuard l(x_sync); 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); onPeerHashes(_peer, _hashes, false);
} }
@ -399,7 +406,7 @@ void EthereumHost::onPeerDoneHashes(EthereumPeer* _peer, bool _localChain)
void EthereumHost::onPeerBlocks(EthereumPeer* _peer, RLP const& _r) void EthereumHost::onPeerBlocks(EthereumPeer* _peer, RLP const& _r)
{ {
RecursiveGuard l(x_sync); RecursiveGuard l(x_sync);
assert(_peer->m_asking == Asking::Nothing); _peer->setAsking(Asking::Nothing);
unsigned itemCount = _r.itemCount(); unsigned itemCount = _r.itemCount();
clog(NetMessageSummary) << "Blocks (" << dec << itemCount << "entries)" << (itemCount ? "" : ": NoMoreBlocks"); clog(NetMessageSummary) << "Blocks (" << dec << itemCount << "entries)" << (itemCount ? "" : ": NoMoreBlocks");
@ -588,7 +595,7 @@ void EthereumHost::onPeerAborting(EthereumPeer* _peer)
void EthereumHost::continueSync() void EthereumHost::continueSync()
{ {
clog(NetAllDetail) << "Getting help with downloading hashes and blocks"; clog(NetAllDetail) << "Continuing sync for all peers";
foreachPeer([&](EthereumPeer* _p) foreachPeer([&](EthereumPeer* _p)
{ {
if (_p->m_asking == Asking::Nothing) if (_p->m_asking == Asking::Nothing)
@ -596,19 +603,19 @@ void EthereumHost::continueSync()
}); });
} }
bool EthereumHost::readyForMore() const
{
auto s = m_bq.status();
return s.verified + s.verifying + s.unverified < 1024 && s.unknown < 1024;
}
void EthereumHost::continueSync(EthereumPeer* _peer) void EthereumHost::continueSync(EthereumPeer* _peer)
{ {
assert(_peer->m_asking == Asking::Nothing); assert(_peer->m_asking == Asking::Nothing);
bool otherPeerV60Sync = false; bool otherPeerV60Sync = false;
bool otherPeerV61Sync = false; bool otherPeerV61Sync = false;
if (m_needSyncHashes && peerShouldGrabChain(_peer)) if (m_needSyncHashes)
{ {
if (!peerShouldGrabChain(_peer))
{
_peer->setIdle();
return;
}
foreachPeer([&](EthereumPeer* _p) foreachPeer([&](EthereumPeer* _p)
{ {
if (_p != _peer && _p->m_asking == Asking::Hashes) if (_p != _peer && _p->m_asking == Asking::Hashes)
@ -656,7 +663,20 @@ void EthereumHost::continueSync(EthereumPeer* _peer)
} }
else if (m_needSyncBlocks && peerCanHelp(_peer)) // Check if this peer can help with downloading blocks else if (m_needSyncBlocks && peerCanHelp(_peer)) // Check if this peer can help with downloading blocks
{ {
if (readyForMore()) // 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(); _peer->requestBlocks();
} }
else else
@ -709,15 +729,7 @@ bool EthereumHost::peerShouldGrabChain(EthereumPeer* _peer) const
bool EthereumHost::isSyncing_UNSAFE() 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. return m_needSyncBlocks || m_needSyncHashes;
/// 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;
} }
HashChainStatus EthereumHost::status() HashChainStatus EthereumHost::status()
@ -725,6 +737,6 @@ HashChainStatus EthereumHost::status()
RecursiveGuard l(x_sync); RecursiveGuard l(x_sync);
if (m_syncingV61) if (m_syncingV61)
return HashChainStatus { static_cast<unsigned>(m_hashMan.chainSize()), static_cast<unsigned>(m_hashMan.gotCount()), false }; 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,12 +125,13 @@ private:
bool peerShouldGrabBlocks(EthereumPeer* _peer) const; bool peerShouldGrabBlocks(EthereumPeer* _peer) const;
bool peerShouldGrabChain(EthereumPeer* _peer) const; bool peerShouldGrabChain(EthereumPeer* _peer) const;
bool peerCanHelp(EthereumPeer* _peer) const; bool peerCanHelp(EthereumPeer* _peer) const;
unsigned estimateHashes();
void estimatePeerHashes(EthereumPeer* _peer); void estimatePeerHashes(EthereumPeer* _peer);
bool readyForMore() const;
BlockChain const& m_chain; BlockChain const& m_chain;
TransactionQueue& m_tq; ///< Maintains a list of incoming transactions not yet in a block on the blockchain. TransactionQueue& m_tq; ///< Maintains a list of incoming transactions not yet in a block on the blockchain.
BlockQueue& m_bq; ///< Maintains a list of incoming blocks not yet on the blockchain (to be imported). BlockQueue& m_bq; ///< Maintains a list of incoming blocks not yet on the blockchain (to be imported).
Handler m_bqRoomAvailable;
u256 m_networkId; u256 m_networkId;

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

8
libethereum/Executive.cpp

@ -201,12 +201,12 @@ void Executive::initialize(Transaction const& _transaction)
// Avoid unaffordable transactions. // Avoid unaffordable transactions.
m_gasCost = (bigint)m_t.gas() * m_t.gasPrice(); m_gasCost = (bigint)m_t.gas() * m_t.gasPrice();
m_totalCost = m_t.value() + m_gasCost; bigint totalCost = m_t.value() + m_gasCost;
if (m_s.balance(m_t.sender()) < m_totalCost) 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; 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()));
} }
} }

6
libethereum/Executive.h

@ -86,8 +86,6 @@ public:
Executive(State& _s, LastHashes const& _lh, unsigned _level = 0): m_s(_s), m_lastHashes(_lh), m_depth(_level) {} Executive(State& _s, LastHashes const& _lh, unsigned _level = 0): m_s(_s), m_lastHashes(_lh), m_depth(_level) {}
/// Basic constructor. /// Basic constructor.
Executive(State& _s, BlockChain const& _bc, unsigned _level = 0); Executive(State& _s, BlockChain const& _bc, unsigned _level = 0);
/// Basic destructor.
~Executive() = default;
Executive(Executive const&) = delete; Executive(Executive const&) = delete;
void operator=(Executive) = delete; void operator=(Executive) = delete;
@ -145,7 +143,7 @@ public:
private: private:
State& m_s; ///< The state to which this operation/transaction is applied. State& m_s; ///< The state to which this operation/transaction is applied.
LastHashes m_lastHashes; 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. bytesRef m_outRef; ///< Reference to "expected output" buffer.
ExecutionResult* m_res = nullptr; ///< Optional storage for execution results. 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. Address m_newAddress; ///< The address of the created contract in the case of create() being called.
@ -158,9 +156,7 @@ private:
Transaction m_t; ///< The original transaction. Set by setup(). Transaction m_t; ///< The original transaction. Set by setup().
LogEntries m_logs; ///< The log entries created by this transaction. Set by finalize(). 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_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 std::pair<h256, unsigned> transactionLocation(h256 const& _transactionHash) const = 0;
virtual h256 hashFromNumber(BlockNumber _number) const = 0; virtual h256 hashFromNumber(BlockNumber _number) const = 0;
virtual BlockNumber numberFromHash(h256 _blockHash) 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 BlockInfo blockInfo(h256 _hash) const = 0;
virtual BlockDetails blockDetails(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()); 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 bool LogFilter::matches(LogBloom _bloom) const
{ {
if (m_addresses.size()) if (m_addresses.size())

15
libethereum/LogFilter.h

@ -45,15 +45,14 @@ class State;
class LogFilter class LogFilter
{ {
public: 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; void streamRLP(RLPStream& _s) const;
h256 sha3() const; h256 sha3() const;
unsigned earliest() const { return m_earliest; } h256 earliest() const { return m_earliest; }
unsigned latest() const { return m_latest; } h256 latest() const { return m_latest; }
bool envelops(RelativeBlock _logBlockRelation, u256 _logBlockNumber) const;
std::vector<LogBloom> bloomPossibilities() const; std::vector<LogBloom> bloomPossibilities() const;
bool matches(LogBloom _bloom) const; bool matches(LogBloom _bloom) const;
bool matches(State const& _s, unsigned _i) 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 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 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 withEarliest(h256 _e) { m_earliest = _e; return *this; }
LogFilter withLatest(int _e) { m_latest = _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); friend std::ostream& dev::eth::operator<<(std::ostream& _out, dev::eth::LogFilter const& _s);
private: private:
AddressHash m_addresses; AddressHash m_addresses;
std::array<h256Hash, 4> m_topics; std::array<h256Hash, 4> m_topics;
unsigned m_earliest = 0; h256 m_earliest = EarliestBlockHash;
unsigned m_latest = LatestBlock; h256 m_latest = PendingBlockHash;
}; };
} }

6
libethereum/State.cpp

@ -778,7 +778,8 @@ void State::cleanup(bool _fullCommit)
paranoia("immediately before database commit", true); paranoia("immediately before database commit", true);
// Commit the new trie to disk. // 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 { try {
EnforceRefs er(m_db, true); EnforceRefs er(m_db, true);
@ -791,7 +792,8 @@ void State::cleanup(bool _fullCommit)
} }
m_db.commit(); 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); paranoia("immediately after database commit", true);
m_previousBlock = m_currentBlock; m_previousBlock = m_currentBlock;

26
libevm/ExtVMFace.h

@ -63,10 +63,28 @@ using LogEntries = std::vector<LogEntry>;
struct LocalisedLogEntry: public LogEntry struct LocalisedLogEntry: public LogEntry
{ {
LocalisedLogEntry() {} LocalisedLogEntry() {}
LocalisedLogEntry(LogEntry const& _le, unsigned _number, h256 _transactionHash = h256()): LogEntry(_le), number(_number), transactionHash(_transactionHash) {} explicit LocalisedLogEntry(LogEntry const& _le): LogEntry(_le) {};
unsigned number = 0; explicit LocalisedLogEntry(
h256 transactionHash; 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>; using LocalisedLogEntries = std::vector<LocalisedLogEntry>;

2
libjsqrc/ethereumjs/.jshintrc

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

7
libjsqrc/ethereumjs/.versions

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

5
libjsqrc/ethereumjs/bower.json

@ -1,14 +1,15 @@
{ {
"name": "web3", "name": "web3",
"namespace": "ethereum", "namespace": "ethereum",
"version": "0.4.2", "version": "0.5.0",
"description": "Ethereum Compatible JavaScript API", "description": "Ethereum Compatible JavaScript API",
"main": [ "main": [
"./dist/web3.js", "./dist/web3.js",
"./dist/web3.min.js" "./dist/web3.min.js"
], ],
"dependencies": { "dependencies": {
"bignumber.js": ">=2.0.0" "bignumber.js": ">=2.0.0",
"crypto-js": "~3.1.4"
}, },
"repository": { "repository": {
"type": "git", "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" +
"}\n"; "}\n";
var code = web3.eth.compile.solidity(source).code; var compiled = web3.eth.compile.solidity(source);
/*var code = "605280600c6000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063c6888fa114602e57005b60376004356041565b8060005260206000f35b6000600782029050604d565b91905056";*/ var code = compiled.test.code;
// contract json abi, this is autogenerated using solc CLI
// contract description, this is autogenerated using solc CLI var abi = compiled.test.info.abiDefinition;
var desc = [{
"constant" : true,
"inputs" : [{
"name" : "a",
"type" : "uint256"
}],
"name" : "multiply",
"outputs" : [{
"name" : "d",
"type" : "uint256"
}],
"type" : "function"
}];
var myContract; var myContract;
@ -47,7 +34,7 @@
var watch = web3.eth.filter('latest'); var watch = web3.eth.filter('latest');
// create contract // create contract
myContract = web3.eth.contract(desc).new({data: code}); myContract = web3.eth.contract(abi).new({data: code});
console.log('address: ' + myContract.address); console.log('address: ' + myContract.address);
document.getElementById('status').innerText = "transaction sent, waiting for confirmation"; document.getElementById('status').innerText = "transaction sent, waiting for confirmation";
watch.watch(function (err, hash) { watch.watch(function (err, hash) {

29
libjsqrc/ethereumjs/example/event_inc.html

@ -18,29 +18,10 @@
" } " + " } " +
" uint x; " + " uint x; " +
"}"; "}";
var code = web3.eth.compile.solidity(source).code;
/*var code = "5b60456000600050819055505b608c8060196000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063371303c014602e57005b6034603a565b60006000f35b6000600081815054600101919050819055506001600260006000505406147f6e61ef44ac2747ff8b84d353a908eb8bd5c3fb118334d57698c5cfc7041196ad600060006000505481526020016000a25b56";*/ var compiled = web3.eth.compile.solidity(source);
var code = compiled.Contract.code;
var desc = [{ var abi = compiled.Contract.info.abiDefinition;
"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 address; var address;
var contract; var contract;
@ -55,7 +36,7 @@
var watch = web3.eth.filter('latest'); 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); 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.HttpProvider = require('./lib/web3/httpprovider');
web3.providers.QtSyncProvider = require('./lib/web3/qtsync'); web3.providers.QtSyncProvider = require('./lib/web3/qtsync');
web3.eth.contract = require('./lib/web3/contract'); 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 // dont override global variable
if (typeof window !== 'undefined' && typeof window.web3 === 'undefined') { 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} * @returns {Boolean}
*/ */
SolidityParam.prototype.isDynamic = function () { 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); var offset = getOffset(bytes, index);
// 2 * , cause we also parse length // 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; index = index || 0;
var offset = getOffset(bytes, index); var offset = getOffset(bytes, index);
var length = parseInt('0x' + bytes.substr(offset * 2, 64)); 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; module.exports = SolidityParam;

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

@ -38,11 +38,19 @@ var BigNumber = require('bignumber.js');
var ETH_UNITS = [ var ETH_UNITS = [
'wei', 'wei',
'Kwei', 'kwei',
'Mwei', 'Mwei',
'Gwei', 'Gwei',
'szabo', 'szabo',
'finney', 'finney',
'femtoether',
'picoether',
'nanoether',
'microether',
'milliether',
'nano',
'micro',
'milli',
'ether', 'ether',
'grand', 'grand',
'Mether', '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 BigNumber = require('bignumber.js');
var unitMap = { var unitMap = {
'wei': '1', 'wei': '1',
'kwei': '1000', 'kwei': '1000',
'ada': '1000', 'ada': '1000',
'mwei': '1000000', 'femtoether': '1000',
'babbage': '1000000', 'mwei': '1000000',
'gwei': '1000000000', 'babbage': '1000000',
'shannon': '1000000000', 'picoether': '1000000',
'szabo': '1000000000000', 'gwei': '1000000000',
'finney': '1000000000000000', 'shannon': '1000000000',
'ether': '1000000000000000000', 'nanoether': '1000000000',
'kether': '1000000000000000000000', 'nano': '1000000000',
'grand': '1000000000000000000000', 'szabo': '1000000000000',
'einstein': '1000000000000000000000', 'microether': '1000000000000',
'mether': '1000000000000000000000000', 'micro': '1000000000000',
'gether': '1000000000000000000000000000', 'finney': '1000000000000000',
'tether': '1000000000000000000000000000000' '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. * Takes a number of wei and converts it to any other ether unit.
* *
* Possible units are: * Possible units are:
* - kwei/ada * SI Short SI Full Effigy Other
* - mwei/babbage * - kwei femtoether ada
* - gwei/shannon * - mwei picoether babbage
* - szabo * - gwei nanoether shannon nano
* - finney * - -- microether szabo micro
* - ether * - -- milliether finney milli
* - kether/grand/einstein * - ether -- --
* - kether einstein grand
* - mether * - mether
* - gether * - gether
* - tether * - tether
@ -265,13 +274,14 @@ var fromWei = function(number, unit) {
* Takes a number of a unit and converts it to wei. * Takes a number of a unit and converts it to wei.
* *
* Possible units are: * Possible units are:
* - kwei/ada * SI Short SI Full Effigy Other
* - mwei/babbage * - kwei femtoether ada
* - gwei/shannon * - mwei picoether babbage
* - szabo * - gwei nanoether shannon nano
* - finney * - -- microether szabo micro
* - ether * - -- milliether finney milli
* - kether/grand/einstein * - ether -- --
* - kether einstein grand
* - mether * - mether
* - gether * - gether
* - tether * - 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 = { module.exports = {
padLeft: padLeft, padLeft: padLeft,
toHex: toHex, toHex: toHex,
@ -470,6 +492,7 @@ module.exports = {
isObject: isObject, isObject: isObject,
isBoolean: isBoolean, isBoolean: isBoolean,
isArray: isArray, 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 formatters = require('./web3/formatters');
var RequestManager = require('./web3/requestmanager'); var RequestManager = require('./web3/requestmanager');
var c = require('./utils/config'); var c = require('./utils/config');
var Method = require('./web3/method');
var Property = require('./web3/property'); var Property = require('./web3/property');
var Batch = require('./web3/batch'); var Batch = require('./web3/batch');
var sha3 = require('./utils/sha3');
var web3Methods = [
new Method({
name: 'sha3',
call: 'web3_sha3',
params: 1
})
];
var web3Properties = [ var web3Properties = [
new Property({ new Property({
@ -130,6 +122,8 @@ web3.toBigNumber = utils.toBigNumber;
web3.toWei = utils.toWei; web3.toWei = utils.toWei;
web3.fromWei = utils.fromWei; web3.fromWei = utils.fromWei;
web3.isAddress = utils.isAddress; web3.isAddress = utils.isAddress;
web3.isIBAN = utils.isIBAN;
web3.sha3 = sha3;
web3.createBatch = function () { web3.createBatch = function () {
return new Batch(); return new Batch();
}; };
@ -156,7 +150,6 @@ Object.defineProperty(web3.eth, 'defaultAccount', {
}); });
/// setups all api methods /// setups all api methods
setupMethods(web3, web3Methods);
setupProperties(web3, web3Properties); setupProperties(web3, web3Properties);
setupMethods(web3.net, net.methods); setupMethods(web3.net, net.methods);
setupProperties(web3.net, net.properties); 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 coder = require('../solidity/coder');
var web3 = require('../web3'); var web3 = require('../web3');
var formatters = require('./formatters'); var formatters = require('./formatters');
var sha3 = require('../utils/sha3');
/** /**
* This prototype should be used to create event filters * This prototype should be used to create event filters
@ -77,7 +78,7 @@ SolidityEvent.prototype.typeName = function () {
* @return {String} event signature * @return {String} event signature
*/ */
SolidityEvent.prototype.signature = function () { 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 web3 = require('../web3');
var coder = require('../solidity/coder'); var coder = require('../solidity/coder');
var utils = require('../utils/utils'); var utils = require('../utils/utils');
var sha3 = require('../utils/sha3');
/** /**
* This prototype should be used to call/sendTransaction to solidity functions * This prototype should be used to call/sendTransaction to solidity functions
@ -69,12 +70,12 @@ SolidityFunction.prototype.toPayload = function (args) {
* @return {String} function signature * @return {String} function signature
*/ */
SolidityFunction.prototype.signature = function () { 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) { SolidityFunction.prototype.unpackOutput = function (output) {
if (output === null) { if (!output) {
return; return;
} }
@ -94,7 +95,7 @@ SolidityFunction.prototype.unpackOutput = function (output) {
* @return {String} output bytes * @return {String} output bytes
*/ */
SolidityFunction.prototype.call = function () { 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 callback = this.extractCallback(args);
var payload = this.toPayload(args); var payload = this.toPayload(args);
@ -116,18 +117,35 @@ SolidityFunction.prototype.call = function () {
* @param {Object} options * @param {Object} options
*/ */
SolidityFunction.prototype.sendTransaction = function () { 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 callback = this.extractCallback(args);
var payload = this.toPayload(args); var payload = this.toPayload(args);
if (!callback) { if (!callback) {
web3.eth.sendTransaction(payload); return web3.eth.sendTransaction(payload);
return;
} }
web3.eth.sendTransaction(payload, callback); 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 * Should be used to get function display name
* *
@ -195,6 +213,7 @@ SolidityFunction.prototype.attachToContract = function (contract) {
execute.request = this.request.bind(this); execute.request = this.request.bind(this);
execute.call = this.call.bind(this); execute.call = this.call.bind(this);
execute.sendTransaction = this.sendTransaction.bind(this); execute.sendTransaction = this.sendTransaction.bind(this);
execute.estimateGas = this.estimateGas.bind(this);
var displayName = this.displayName(); var displayName = this.displayName();
if (!contract[displayName]) { if (!contract[displayName]) {
contract[displayName] = execute; 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 */ /* jshint ignore:start */
Package.describe({ Package.describe({
name: 'ethereum:web3', name: 'ethereum:web3',
version: '0.4.2', version: '0.5.0',
summary: 'Ethereum JavaScript API, middleware to talk to a ethreum node over RPC', summary: 'Ethereum JavaScript API, middleware to talk to a ethreum node over RPC',
git: 'https://github.com/ethereum/ethereum.js', git: 'https://github.com/ethereum/ethereum.js',
// By default, Meteor will default to using README.md for documentation. // By default, Meteor will default to using README.md for documentation.

3
libjsqrc/ethereumjs/package.json

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

4
libjsqrc/ethereumjs/test/batch.js

@ -58,10 +58,6 @@ describe('lib/web3/batch', function () {
var address = '0x0000000000000000000000000000000000000000'; var address = '0x0000000000000000000000000000000000000000';
var result = '0x126'; var result = '0x126';
var result2 = '0x0000000000000000000000000000000000000000000000000000000000000123'; var result2 = '0x0000000000000000000000000000000000000000000000000000000000000123';
var signature = '0x001122334455';
// TODO: fix this, maybe in browser sha3?
provider.injectResult(signature);
var counter = 0; var counter = 0;
var callback = function (err, r) { 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' + test({ type: 'bytes', expected: 'gavofyork', value: '0000000000000000000000000000000000000000000000000000000000000020' +
'0000000000000000000000000000000000000000000000000000000000000009' + '0000000000000000000000000000000000000000000000000000000000000009' +
'6761766f66796f726b0000000000000000000000000000000000000000000000'}); '6761766f66796f726b0000000000000000000000000000000000000000000000'});
test({ type: 'int[]', expected: [], value: '0000000000000000000000000000000000000000000000000000000000000020' +
'0000000000000000000000000000000000000000000000000000000000000000'});
test({ type: 'int[]', expected: [new bn(3)], value: '0000000000000000000000000000000000000000000000000000000000000020' + test({ type: 'int[]', expected: [new bn(3)], value: '0000000000000000000000000000000000000000000000000000000000000020' +
'0000000000000000000000000000000000000000000000000000000000000001' + '0000000000000000000000000000000000000000000000000000000000000001' +
'0000000000000000000000000000000000000000000000000000000000000003'}); '0000000000000000000000000000000000000000000000000000000000000003'});

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

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

308
libjsqrc/ethereumjs/test/contract.js

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

22
libsolidity/Compiler.cpp

@ -52,22 +52,26 @@ void Compiler::compileContract(ContractDefinition const& _contract,
map<ContractDefinition const*, bytes const*> const& _contracts) map<ContractDefinition const*, bytes const*> const& _contracts)
{ {
m_context = CompilerContext(); // clear it just in case m_context = CompilerContext(); // clear it just in case
CompilerUtils(m_context).initialiseFreeMemoryPointer();
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); for (Declaration const* function: functions)
function->accept(*this); {
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 the runtime context with the creation-time context
swap(m_context, m_runtimeContext); swap(m_context, m_runtimeContext);
CompilerContext::LocationSetter locationSetterCreationTime(m_context, _contract);
CompilerUtils(m_context).initialiseFreeMemoryPointer(); CompilerUtils(m_context).initialiseFreeMemoryPointer();
initializeContext(_contract, _contracts); initializeContext(_contract, _contracts);
packIntoContractCreator(_contract, m_runtimeContext); packIntoContractCreator(_contract, m_runtimeContext);

146
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) static Json::Value toJson(dev::eth::LocalisedLogEntry const& _e)
{ {
Json::Value res; Json::Value res;
if (_e.transactionHash) if (_e.topics.size() > 0)
{ {
res["data"] = toJS(_e.data); res["data"] = toJS(_e.data);
res["address"] = toJS(_e.address); res["address"] = toJS(_e.address);
res["topics"] = Json::Value(Json::arrayValue); res["topics"] = Json::Value(Json::arrayValue);
for (auto const& t: _e.topics) for (auto const& t: _e.topics)
res["topics"].append(toJS(t)); res["topics"].append(toJS(t));
res["number"] = _e.number; if (_e.mined)
res["hash"] = toJS(_e.transactionHash); {
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; return res;
} }
@ -193,7 +211,7 @@ static Json::Value toJson(map<u256, u256> const& _storage)
return res; 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; dev::eth::LogFilter filter;
if (!_json.isObject() || _json.empty()) if (!_json.isObject() || _json.empty())
@ -201,9 +219,44 @@ static dev::eth::LogFilter toLogFilter(Json::Value const& _json) // commented to
// check only !empty. it should throw exceptions if input params are incorrect // check only !empty. it should throw exceptions if input params are incorrect
if (!_json["fromBlock"].empty()) if (!_json["fromBlock"].empty())
filter.withEarliest(jsToBlockNumber(_json["fromBlock"].asString())); filter.withEarliest(jsToFixed<32>(_json["fromBlock"].asString()));
if (!_json["toBlock"].empty()) if (!_json["toBlock"].empty())
filter.withLatest(jsToBlockNumber(_json["toBlock"].asString())); 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())
return filter;
// check only !empty. it should throw exceptions if input params are incorrect
if (!_json["fromBlock"].empty())
filter.withEarliest(_client.hashFromNumber(jsToBlockNumber(_json["fromBlock"].asString())));
if (!_json["toBlock"].empty())
filter.withLatest(_client.hashFromNumber(jsToBlockNumber(_json["toBlock"].asString())));
if (!_json["address"].empty()) if (!_json["address"].empty())
{ {
if (_json["address"].isArray()) if (_json["address"].isArray())
@ -712,25 +765,25 @@ Json::Value WebThreeStubServerBase::eth_getCompilers()
} }
string WebThreeStubServerBase::eth_compileLLL(string const& _code) string WebThreeStubServerBase::eth_compileLLL(string const& _source)
{ {
// TODO throw here jsonrpc errors // TODO throw here jsonrpc errors
string res; string res;
vector<string> errors; vector<string> errors;
res = toJS(dev::eth::compileLLL(_code, true, &errors)); res = toJS(dev::eth::compileLLL(_source, true, &errors));
cwarn << "LLL compilation errors: " << errors; cwarn << "LLL compilation errors: " << errors;
return res; return res;
} }
string WebThreeStubServerBase::eth_compileSerpent(string const& _code) string WebThreeStubServerBase::eth_compileSerpent(string const& _source)
{ {
// TODO throw here jsonrpc errors // TODO throw here jsonrpc errors
string res; string res;
(void)_code; (void)_source;
#if ETH_SERPENT || !ETH_TRUE #if ETH_SERPENT || !ETH_TRUE
try try
{ {
res = toJS(dev::asBytes(::compile(_code))); res = toJS(dev::asBytes(::compile(_source)));
} }
catch (string err) catch (string err)
{ {
@ -744,32 +797,66 @@ string WebThreeStubServerBase::eth_compileSerpent(string const& _code)
return res; return res;
} }
string WebThreeStubServerBase::eth_compileSolidity(string const& _code) Json::Value WebThreeStubServerBase::eth_compileSolidity(string const& _source)
{ {
// TOOD throw here jsonrpc errors // TOOD throw here jsonrpc errors
(void)_code; Json::Value res(Json::objectValue);
string res;
#if ETH_SOLIDITY || !ETH_TRUE #if ETH_SOLIDITY || !ETH_TRUE
dev::solidity::CompilerStack compiler; dev::solidity::CompilerStack compiler;
try 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) catch (dev::Exception const& exception)
{ {
ostringstream error; ostringstream error;
solidity::SourceReferenceFormatter::printExceptionInformation(error, exception, "Error", compiler); solidity::SourceReferenceFormatter::printExceptionInformation(error, exception, "Error", compiler);
cwarn << "Solidity compilation error: " << error.str(); cwarn << "Solidity compilation error: " << error.str();
return Json::Value(Json::objectValue);
} }
catch (...) catch (...)
{ {
cwarn << "Uncought solidity compilation exception"; cwarn << "Uncought solidity compilation exception";
return Json::Value(Json::objectValue);
} }
#endif #endif
return res; return res;
} }
string WebThreeStubServerBase::eth_newFilter(Json::Value const& _json) 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 try
{ {
@ -823,6 +910,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) Json::Value WebThreeStubServerBase::eth_getFilterLogs(string const& _filterId)
{ {
try try
@ -835,6 +938,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) Json::Value WebThreeStubServerBase::eth_getLogs(Json::Value const& _json)
{ {
try try
@ -980,7 +1095,6 @@ string WebThreeStubServerBase::shh_addToGroup(string const& _group, string const
string WebThreeStubServerBase::shh_newFilter(Json::Value const& _json) string WebThreeStubServerBase::shh_newFilter(Json::Value const& _json)
{ {
try try
{ {
pair<shh::Topics, Public> w = toWatch(_json); pair<shh::Topics, Public> w = toWatch(_json);

7
libweb3jsonrpc/WebThreeStubServerBase.h

@ -60,8 +60,6 @@ public:
/** /**
* @brief JSON-RPC api implementation * @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 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. * @todo modularise everything so additional subprotocols don't need to change this file.
*/ */
@ -105,13 +103,16 @@ public:
virtual Json::Value eth_getCompilers(); virtual Json::Value eth_getCompilers();
virtual std::string eth_compileLLL(std::string const& _s); virtual std::string eth_compileLLL(std::string const& _s);
virtual std::string eth_compileSerpent(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_newFilter(Json::Value const& _json);
virtual std::string eth_newFilterEx(Json::Value const& _json);
virtual std::string eth_newBlockFilter(); virtual std::string eth_newBlockFilter();
virtual std::string eth_newPendingTransactionFilter(); virtual std::string eth_newPendingTransactionFilter();
virtual bool eth_uninstallFilter(std::string const& _filterId); virtual bool eth_uninstallFilter(std::string const& _filterId);
virtual Json::Value eth_getFilterChanges(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_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_getLogs(Json::Value const& _json);
virtual Json::Value eth_getWork(); virtual Json::Value eth_getWork();
virtual bool eth_submitWork(std::string const& _nonce, std::string const&, std::string const& _mixHash); 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_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_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_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_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_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_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_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_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_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_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_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); 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]); 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) inline virtual void eth_newBlockFilterI(const Json::Value &request, Json::Value &response)
{ {
(void)request; (void)request;
@ -245,10 +252,18 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServer<AbstractWebThr
{ {
response = this->eth_getFilterChanges(request[0u].asString()); 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) inline virtual void eth_getFilterLogsI(const Json::Value &request, Json::Value &response)
{ {
response = this->eth_getFilterLogs(request[0u].asString()); 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) inline virtual void eth_getLogsI(const Json::Value &request, Json::Value &response)
{ {
response = this->eth_getLogs(request[0u]); response = this->eth_getLogs(request[0u]);
@ -364,13 +379,16 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServer<AbstractWebThr
virtual Json::Value eth_getCompilers() = 0; virtual Json::Value eth_getCompilers() = 0;
virtual std::string eth_compileLLL(const std::string& param1) = 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_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_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_newBlockFilter() = 0;
virtual std::string eth_newPendingTransactionFilter() = 0; virtual std::string eth_newPendingTransactionFilter() = 0;
virtual bool eth_uninstallFilter(const std::string& param1) = 0; virtual bool eth_uninstallFilter(const std::string& param1) = 0;
virtual Json::Value eth_getFilterChanges(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_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_getLogs(const Json::Value& param1) = 0;
virtual Json::Value eth_getWork() = 0; virtual Json::Value eth_getWork() = 0;
virtual bool eth_submitWork(const std::string& param1, const std::string& param2, const std::string& param3) = 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_getCompilers", "params": [], "order": [], "returns": []},
{ "name": "eth_compileLLL", "params": [""], "order": [], "returns": ""}, { "name": "eth_compileLLL", "params": [""], "order": [], "returns": ""},
{ "name": "eth_compileSerpent", "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_newFilter", "params": [{}], "order": [], "returns": ""},
{ "name": "eth_newFilterEx", "params": [{}], "order": [], "returns": ""},
{ "name": "eth_newBlockFilter", "params": [], "order": [], "returns": ""}, { "name": "eth_newBlockFilter", "params": [], "order": [], "returns": ""},
{ "name": "eth_newPendingTransactionFilter", "params": [], "order": [], "returns": ""}, { "name": "eth_newPendingTransactionFilter", "params": [], "order": [], "returns": ""},
{ "name": "eth_uninstallFilter", "params": [""], "order": [], "returns": true}, { "name": "eth_uninstallFilter", "params": [""], "order": [], "returns": true},
{ "name": "eth_getFilterChanges", "params": [""], "order": [], "returns": []}, { "name": "eth_getFilterChanges", "params": [""], "order": [], "returns": []},
{ "name": "eth_getFilterChangesEx", "params": [""], "order": [], "returns": []},
{ "name": "eth_getFilterLogs", "params": [""], "order": [], "returns": []}, { "name": "eth_getFilterLogs", "params": [""], "order": [], "returns": []},
{ "name": "eth_getFilterLogsEx", "params": [""], "order": [], "returns": []},
{ "name": "eth_getLogs", "params": [{}], "order": [], "returns": []}, { "name": "eth_getLogs", "params": [{}], "order": [], "returns": []},
{ "name": "eth_getWork", "params": [], "order": [], "returns": []}, { "name": "eth_getWork", "params": [], "order": [], "returns": []},
{ "name": "eth_submitWork", "params": ["", "", ""], "order": [], "returns": true}, { "name": "eth_submitWork", "params": ["", "", ""], "order": [], "returns": true},

16
mix/MixClient.cpp

@ -81,6 +81,8 @@ void MixClient::resetState(std::unordered_map<Address, Account> const& _accounts
WriteGuard l(x_state); WriteGuard l(x_state);
Guard fl(x_filtersWatches); Guard fl(x_filtersWatches);
m_filters.clear(); m_filters.clear();
for (auto& i: m_specialFilters)
i.second.clear();
m_watches.clear(); m_watches.clear();
m_stateDB = OverlayDB(); m_stateDB = OverlayDB();
@ -254,7 +256,7 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c
h256Set changed; h256Set changed;
Guard l(x_filtersWatches); Guard l(x_filtersWatches);
for (std::pair<h256 const, eth::InstalledFilter>& i: m_filters) for (std::pair<h256 const, eth::InstalledFilter>& i: m_filters)
if ((unsigned)i.second.filter.latest() > bc().number()) if (compareBlockHashes(i.second.filter.latest(), bc().currentHash()) > 0)
{ {
// acceptable number. // acceptable number.
auto m = i.second.filter.matches(_state.receipt(_state.pending().size() - 1)); auto m = i.second.filter.matches(_state.receipt(_state.pending().size() - 1));
@ -262,11 +264,12 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c
{ {
// filter catches them // filter catches them
for (LogEntry const& l: m) for (LogEntry const& l: m)
i.second.changes.push_back(LocalisedLogEntry(l, bc().number() + 1)); i.second.changes.push_back(LocalisedLogEntry(l));
changed.insert(i.first); changed.insert(i.first);
} }
} }
changed.insert(dev::eth::PendingChangedFilter); changed.insert(dev::eth::PendingChangedFilter);
m_specialFilters.at(dev::eth::PendingChangedFilter).push_back(t.sha3());
noteChanged(changed); noteChanged(changed);
} }
WriteGuard l(x_executions); WriteGuard l(x_executions);
@ -281,7 +284,7 @@ void MixClient::mine()
bc().import(m_state.blockData(), m_state.db(), ImportRequirements::Default & ~ImportRequirements::ValidNonce); bc().import(m_state.blockData(), m_state.db(), ImportRequirements::Default & ~ImportRequirements::ValidNonce);
m_state.sync(bc()); m_state.sync(bc());
m_startState = m_state; m_startState = m_state;
h256Set changed { dev::eth::PendingChangedFilter, dev::eth::ChainChangedFilter }; h256Set changed { dev::eth::ChainChangedFilter };
noteChanged(changed); noteChanged(changed);
} }
@ -376,11 +379,14 @@ void MixClient::noteChanged(h256Set const& _filters)
{ {
if (m_filters.count(i.second.id)) if (m_filters.count(i.second.id))
i.second.changes += m_filters.at(i.second.id).changes; i.second.changes += m_filters.at(i.second.id).changes;
else else if (m_specialFilters.count(i.second.id))
i.second.changes.push_back(LocalisedLogEntry(SpecialLogEntry, 0)); for (h256 const& hash: m_specialFilters.at(i.second.id))
i.second.changes.push_back(LocalisedLogEntry(SpecialLogEntry, hash));
} }
for (auto& i: m_filters) for (auto& i: m_filters)
i.second.changes.clear(); i.second.changes.clear();
for (auto& i: m_specialFilters)
i.second.clear();
} }
eth::BlockInfo MixClient::blockInfo() const eth::BlockInfo MixClient::blockInfo() const

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); 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 // export logs
m_TestObject["logs"] = exportLog(_statePost.pending().size() ? _statePost.log(0) : LogEntries()); m_TestObject["logs"] = exportLog(_statePost.pending().size() ? _statePost.log(0) : LogEntries());

3
test/libethereum/StateTestsFiller/stPreCompiledContractsFiller.json

@ -3644,6 +3644,5 @@
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"data" : "" "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); 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) BOOST_AUTO_TEST_CASE(stLogTests)
{ {
dev::test::executeTests("stLogTests", "/StateTests",dev::test::getFolder(__FILE__) + "/StateTestsFiller", dev::test::doStateTests); 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"); shared_ptr<string const> n = make_shared<string>("source");
AssemblyItems items = compileContract(sourceCode); AssemblyItems items = compileContract(sourceCode);
vector<SourceLocation> locations = vector<SourceLocation> locations =
vector<SourceLocation>(11, SourceLocation(2, 75, n)) + vector<SourceLocation>(17, SourceLocation(2, 75, n)) +
vector<SourceLocation>(12, SourceLocation(20, 72, n)) + vector<SourceLocation>(14, SourceLocation(20, 72, n)) +
vector<SourceLocation>{SourceLocation(42, 51, n), SourceLocation(65, 67, n)} + vector<SourceLocation>{SourceLocation(42, 51, n), SourceLocation(65, 67, n)} +
vector<SourceLocation>(4, SourceLocation(58, 67, n)) + vector<SourceLocation>(4, SourceLocation(58, 67, n)) +
vector<SourceLocation>(3, SourceLocation(20, 72, n)); vector<SourceLocation>(3, SourceLocation(20, 72, n));

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

Loading…
Cancel
Save