Browse Source

Merge remote-tracking branch 'upstream/develop' into pr/debug_stack_size

cl-refactor
Paweł Bylica 10 years ago
parent
commit
41fe1726a6
  1. 15
      CMakeLists.txt
  2. 2
      alethzero/Main.ui
  3. 4
      alethzero/MainWin.cpp
  4. 2
      alethzero/OurWebThreeStubServer.cpp
  5. 3
      eth/main.cpp
  6. 39
      ethminer/MinerAux.h
  7. 21
      libdevcore/FixedHash.h
  8. 22
      libdevcore/TrieCommon.h
  9. 1
      libethash-cl/CMakeLists.txt
  10. 70
      libethash-cl/ethash_cl_miner.cpp
  11. 16
      libethash-cl/ethash_cl_miner.h
  12. 16
      libethcore/Common.h
  13. 30
      libethcore/Ethash.cpp
  14. 11
      libethcore/Ethash.h
  15. 14
      libethcore/Transaction.cpp
  16. 4
      libethcore/Transaction.h
  17. 4
      libethereum/Account.h
  18. 8
      libethereum/BlockChain.cpp
  19. 11
      libethereum/BlockChainSync.cpp
  20. 5
      libethereum/BlockChainSync.h
  21. 8
      libethereum/BlockQueue.h
  22. 15
      libethereum/Client.cpp
  23. 6
      libethereum/Client.h
  24. 43
      libethereum/ClientBase.cpp
  25. 9
      libethereum/ClientBase.h
  26. 79
      libethereum/EthereumHost.cpp
  27. 2
      libethereum/EthereumHost.h
  28. 9
      libethereum/Executive.cpp
  29. 4
      libethereum/Executive.h
  30. 2
      libethereum/ExtVM.cpp
  31. 29
      libethereum/Interface.cpp
  32. 12
      libethereum/Interface.h
  33. 6
      libethereum/State.cpp
  34. 2
      libethereum/State.h
  35. 7
      libethereum/Transaction.h
  36. 153
      libethereum/TransactionQueue.cpp
  37. 48
      libethereum/TransactionQueue.h
  38. 2
      libethereum/VerifiedBlock.h
  39. 6
      libevm/ExtVMFace.cpp
  40. 2
      libevm/ExtVMFace.h
  41. 1
      libjsengine/CMakeLists.txt
  42. 7
      libp2p/Host.h
  43. 7
      libp2p/Session.cpp
  44. 10
      libtestutils/StateLoader.cpp
  45. 4
      libweb3jsonrpc/AccountHolder.cpp
  46. 3
      libweb3jsonrpc/JsonHelper.cpp
  47. 2
      libweb3jsonrpc/WebThreeStubServerBase.cpp
  48. 8
      mix/DebuggingStateWrapper.h
  49. 2
      mix/HttpServer.h
  50. 28
      mix/MixClient.cpp
  51. 6
      mix/MixClient.h
  52. 4
      test/TestHelper.cpp
  53. 106
      test/libdevcore/FixedHash.cpp
  54. 854
      test/libethereum/StateTestsFiller/stPreCompiledContractsTransactionFiller.json
  55. 1
      test/libethereum/blockchain.cpp
  56. 5
      test/libethereum/state.cpp
  57. 248
      test/libsolidity/SolidityWallet.cpp

15
CMakeLists.txt

@ -203,7 +203,9 @@ eth_format_option(ROCKSDB)
eth_format_option(GUI)
eth_format_option(TESTS)
eth_format_option(NOBOOST)
eth_format_option(ROCKSDB)
eth_format_option(TOOLS)
eth_format_option(ETHKEY)
eth_format_option(ETHASHCL)
eth_format_option(JSCONSOLE)
eth_format_option_on_decent_platform(SERPENT)
@ -234,6 +236,15 @@ elseif (BUNDLE STREQUAL "full")
set(TOOLS ON)
set(TESTS ON)
set(FATDB ON)
elseif (BUNDLE STREQUAL "cli")
set(SERPENT ${DECENT_PLATFORM})
set(SOLIDITY ON)
set(USENPM ON)
set(GUI OFF)
# set(NCURSES ${DECENT_PLATFORM})
set(TOOLS ON)
set(TESTS ON)
set(FATDB ON)
elseif (BUNDLE STREQUAL "core")
set(SERPENT OFF)
set(SOLIDITY ON)
@ -312,6 +323,7 @@ message("-- Hardware identification support ${CPUID_FO
message("-- HTTP Request support ${CURL_FOUND}")
message("-- VMTRACE VM execution tracing ${VMTRACE}")
message("-- PROFILING Profiling support ${PROFILING}")
message("-- NOBOOST No BOOST macros in test functions ${NOBOOST}")
message("-- FATDB Full database exploring ${FATDB}")
message("-- JSONRPC JSON-RPC support ${JSONRPC}")
message("-- USENPM Javascript source building ${USENPM}")
@ -325,7 +337,6 @@ message("-- SERPENT Build Serpent language components ${SERPENT}
message("-- GUI Build GUI components ${GUI}")
message("-- NCURSES Build NCurses components ${NCURSES}")
message("-- TESTS Build tests ${TESTS}")
message("-- NOBOOST No BOOST macros in test functions ${NOBOOST}")
message("-- ETHASHCL Build OpenCL components (experimental!) ${ETHASHCL}")
message("-- JSCONSOLE Build with javascript console ${JSCONSOLE}")
message("-- EVMJIT Build LLVM-based JIT EVM (experimental!) ${EVMJIT}")
@ -362,8 +373,6 @@ else ()
set(GENERAL 0)
endif ()
message("GENERAL ${GENERAL}")
add_subdirectory(libdevcore)
if (GENERAL)
add_subdirectory(libevmcore)

2
alethzero/Main.ui

@ -707,7 +707,7 @@
</layout>
</widget>
</widget>
<widget class="QDockWidget" name="dockWidget_8">
<widget class="QDockWidget" name="blockChainDockWidget">
<property name="features">
<set>QDockWidget::DockWidgetFeatureMask</set>
</property>

4
alethzero/MainWin.cpp

@ -260,6 +260,8 @@ Main::Main(QWidget *parent) :
m_transact->setWindowFlags(Qt::Dialog);
m_transact->setWindowModality(Qt::WindowModal);
connect(ui->blockChainDockWidget, &QDockWidget::visibilityChanged, [=]() { refreshBlockChain(); });
#if !ETH_FATDB
removeDockWidget(ui->dockWidget_accounts);
#endif
@ -1307,7 +1309,7 @@ void Main::on_turboMining_triggered()
void Main::refreshBlockChain()
{
if (!ui->blocks->isVisible() && isVisible())
if (!(ui->blockChainDockWidget->isVisible() || !tabifiedDockWidgets(ui->blockChainDockWidget).isEmpty()))
return;
DEV_TIMED_FUNCTION_ABOVE(500);

2
alethzero/OurWebThreeStubServer.cpp

@ -130,7 +130,7 @@ void OurAccountHolder::doValidations()
else
// sign and submit.
if (Secret s = m_main->retrieveSecret(t.from))
m_main->ethereum()->submitTransaction(s, t);
m_main->ethereum()->submitTransaction(t, s);
}
}

3
eth/main.cpp

@ -37,6 +37,7 @@
#include <libevm/VM.h>
#include <libevm/VMFactory.h>
#include <libethereum/All.h>
#include <libethereum/BlockChainSync.h>
#include <libethcore/KeyManager.h>
#include <libwebthree/WebThree.h>
@ -375,6 +376,8 @@ void interactiveMode(eth::Client* c, std::shared_ptr<eth::TrivialGasPricer> gasP
cout << "Current block: " << c->blockChain().details().number << endl;
else if (c && cmd == "blockqueue")
cout << "Current blockqueue status: " << endl << c->blockQueueStatus() << endl;
else if (c && cmd == "sync")
cout << "Current sync status: " << endl << c->syncStatus() << endl;
else if (c && cmd == "hashrate")
cout << "Current hash rate: " << toString(c->hashrate()) << " hashes per second." << endl;
else if (c && cmd == "findblock")

39
ethminer/MinerAux.h

@ -128,6 +128,33 @@ public:
cerr << "Bad " << arg << " option: " << argv[i] << endl;
BOOST_THROW_EXCEPTION(BadArgument());
}
else if (arg == "--cl-global-work" && i + 1 < argc)
try {
m_globalWorkSizeMultiplier = stol(argv[++i]);
}
catch (...)
{
cerr << "Bad " << arg << " option: " << argv[i] << endl;
BOOST_THROW_EXCEPTION(BadArgument());
}
else if (arg == "--cl-local-work" && i + 1 < argc)
try {
m_localWorkSize = stol(argv[++i]);
}
catch (...)
{
cerr << "Bad " << arg << " option: " << argv[i] << endl;
BOOST_THROW_EXCEPTION(BadArgument());
}
else if (arg == "--cl-ms-per-batch" && i + 1 < argc)
try {
m_msPerBatch = stol(argv[++i]);
}
catch (...)
{
cerr << "Bad " << arg << " option: " << argv[i] << endl;
BOOST_THROW_EXCEPTION(BadArgument());
}
else if (arg == "--list-devices")
m_shouldListDevices = true;
else if (arg == "--allow-opencl-cpu")
@ -266,16 +293,16 @@ public:
else if (m_minerType == MinerType::GPU)
{
if (!ProofOfWork::GPUMiner::configureGPU(
m_localWorkSize,
m_globalWorkSizeMultiplier,
m_msPerBatch,
m_openclPlatform,
m_openclDevice,
m_clAllowCPU,
m_extraGPUMemory,
m_currentBlock
))
{
cout << "No GPU device with sufficient memory was found. Can't GPU mine. Remove the -G argument" << endl;
exit(1);
}
ProofOfWork::GPUMiner::setNumInstances(m_miningThreads);
}
if (mode == OperationMode::DAGInit)
@ -318,6 +345,9 @@ public:
<< " --list-devices List the detected OpenCL devices and exit." << endl
<< " --current-block Let the miner know the current block number at configuration time. Will help determine DAG size and required GPU memory." << endl
<< " --cl-extragpu-mem Set the memory (in MB) you believe your GPU requires for stuff other than mining. Windows rendering e.t.c.." << endl
<< " --cl-local-work Set the OpenCL local work size. Default is " << toString(dev::eth::Ethash::defaultLocalWorkSize) << endl
<< " --cl-global-work Set the OpenCL global work size as a multiple of the local work size. Default is " << toString(dev::eth::Ethash::defaultGlobalWorkSizeMultiplier) << " * " << toString(dev::eth::Ethash::defaultLocalWorkSize) << endl
<< " --cl-ms-per-batch Set the OpenCL target milliseconds per batch (global workgroup size). Default is " << toString(dev::eth::Ethash::defaultMSPerBatch) << ". If 0 is given then no autoadjustment of global work size will happen" << endl
;
}
@ -506,6 +536,9 @@ private:
unsigned m_miningThreads = UINT_MAX;
bool m_shouldListDevices = false;
bool m_clAllowCPU = false;
unsigned m_globalWorkSizeMultiplier = dev::eth::Ethash::defaultGlobalWorkSizeMultiplier;
unsigned m_localWorkSize = dev::eth::Ethash::defaultLocalWorkSize;
unsigned m_msPerBatch = dev::eth::Ethash::defaultMSPerBatch;
boost::optional<uint64_t> m_currentBlock;
// default value is 350MB of GPU memory for other stuff (windows system rendering, e.t.c.)
unsigned m_extraGPUMemory = 350000000;

21
libdevcore/FixedHash.h

@ -31,6 +31,10 @@
namespace dev
{
/// Compile-time calculation of Log2 of constant values.
template <unsigned N> struct StaticLog2 { enum { result = 1 + StaticLog2<N/2>::result }; };
template <> struct StaticLog2<1> { enum { result = 0 }; };
extern std::random_device s_fixedHashEngine;
/// Fixed-size raw-byte array container type, with an API optimised for storing hashes.
@ -102,7 +106,7 @@ public:
FixedHash operator&(FixedHash const& _c) const { return FixedHash(*this) &= _c; }
FixedHash operator~() const { FixedHash ret; for (unsigned i = 0; i < N; ++i) ret[i] = ~m_data[i]; return ret; }
/// @returns true if all bytes in @a _c are set in this object.
/// @returns true if all one-bits in @a _c are set in this object.
bool contains(FixedHash const& _c) const { return (*this & _c) == _c; }
/// @returns a particular byte from the hash.
@ -171,18 +175,21 @@ public:
template <unsigned P, unsigned M> inline FixedHash<M> bloomPart() const
{
static_assert((M & (M - 1)) == 0, "M must be power-of-two");
static const unsigned c_bloomBits = M * 8;
unsigned mask = c_bloomBits - 1;
unsigned bloomBytes = (dev::toLog2(c_bloomBits) + 7) / 8;
unsigned const c_bloomBits = M * 8;
unsigned const c_mask = c_bloomBits - 1;
unsigned const c_bloomBytes = (StaticLog2<c_bloomBits>::result + 7) / 8;
static_assert((M & (M - 1)) == 0, "M must be power-of-two");
static_assert(P * c_bloomBytes <= N, "out of range");
FixedHash<M> ret;
byte const* p = data();
for (unsigned i = 0; i < P; ++i)
{
unsigned index = 0;
for (unsigned j = 0; j < bloomBytes; ++j, ++p)
for (unsigned j = 0; j < c_bloomBytes; ++j, ++p)
index = (index << 8) | *p;
index &= mask;
index &= c_mask;
ret[M - 1 - index / 8] |= (1 << (index % 8));
}
return ret;

22
libdevcore/TrieCommon.h

@ -32,26 +32,38 @@ inline byte nibble(bytesConstRef _data, unsigned _i)
return (_i & 1) ? (_data[_i / 2] & 15) : (_data[_i / 2] >> 4);
}
inline unsigned sharedNibbles(bytesConstRef _a, unsigned _ab, unsigned _ae, bytesConstRef _b, unsigned _bb, unsigned _be)
/// Interprets @a _first and @a _second as vectors of nibbles and returns the length of the longest common
/// prefix of _first[_beginFirst..._endFirst] and _second[_beginSecond..._endSecond].
inline unsigned sharedNibbles(bytesConstRef _first, unsigned _beginFirst, unsigned _endFirst, bytesConstRef _second, unsigned _beginSecond, unsigned _endSecond)
{
unsigned ret = 0;
for (unsigned ai = _ab, bi = _bb; ai < _ae && bi < _be && nibble(_a, ai) == nibble(_b, bi); ++ai, ++bi, ++ret) {}
while (_beginFirst < _endFirst && _beginSecond < _endSecond && nibble(_first, _beginFirst) == nibble(_second, _beginSecond))
{
++_beginFirst;
++_beginSecond;
++ret;
}
return ret;
}
/**
* Nibble-based view on a bytesConstRef.
*/
struct NibbleSlice
{
bytesConstRef data;
unsigned offset;
NibbleSlice(bytesConstRef _d = bytesConstRef(), unsigned _o = 0): data(_d), offset(_o) {}
NibbleSlice(bytesConstRef _data = bytesConstRef(), unsigned _offset = 0): data(_data), offset(_offset) {}
byte operator[](unsigned _index) const { return nibble(data, offset + _index); }
unsigned size() const { return data.size() * 2 - offset; }
bool empty() const { return !size(); }
NibbleSlice mid(unsigned _index) const { return NibbleSlice(data, offset + _index); }
void clear() { data.reset(); offset = 0; }
/// @returns true iff _k is a prefix of this.
bool contains(NibbleSlice _k) const { return shared(_k) == _k.size(); }
/// @returns the number of shared nibbles at the beginning of this and _k.
unsigned shared(NibbleSlice _k) const { return sharedNibbles(data, offset, offset + size(), _k.data, _k.offset, _k.offset + _k.size()); }
/**
* @brief Determine if we, a full key, are situated prior to a particular key-prefix.
@ -60,8 +72,8 @@ struct NibbleSlice
*/
bool isEarlierThan(NibbleSlice _k) const
{
unsigned i;
for (i = 0; i < _k.size() && i < size(); ++i)
unsigned i = 0;
for (; i < _k.size() && i < size(); ++i)
if (operator[](i) < _k[i]) // Byte is lower - we're earlier..
return true;
else if (operator[](i) > _k[i]) // Byte is higher - we're not earlier.

1
libethash-cl/CMakeLists.txt

@ -20,6 +20,7 @@ file(GLOB OUR_HEADERS "*.h")
set(HEADERS ${OUR_HEADERS} ${CMAKE_CURRENT_BINARY_DIR}/ethash_cl_miner_kernel.h)
include_directories(${CMAKE_CURRENT_BINARY_DIR})
include_directories(${Boost_INCLUDE_DIRS})
include_directories(${OpenCL_INCLUDE_DIRS})
include_directories(..)
add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS})

70
libethash-cl/ethash_cl_miner.cpp

@ -33,6 +33,7 @@
#include <vector>
#include <libethash/util.h>
#include <libethash/ethash.h>
#include <libethcore/Ethash.h>
#include <libethash/internal.h>
#include "ethash_cl_miner.h"
#include "ethash_cl_miner_kernel.h"
@ -49,6 +50,7 @@
#undef max
using namespace std;
using namespace dev::eth;
// 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
@ -140,11 +142,17 @@ unsigned ethash_cl_miner::getNumDevices(unsigned _platformId)
bool ethash_cl_miner::configureGPU(
unsigned _platformId,
unsigned _localWorkSize,
unsigned _globalWorkSize,
unsigned _msPerBatch,
bool _allowCPU,
unsigned _extraGPUMemory,
boost::optional<uint64_t> _currentBlock
)
{
s_workgroupSize = _localWorkSize;
s_initialGlobalWorkSize = _globalWorkSize;
s_msPerBatch = _msPerBatch;
s_allowCPU = _allowCPU;
s_extraRequiredGPUMem = _extraGPUMemory;
// by default let's only consider the DAG of the first epoch
@ -175,6 +183,9 @@ bool ethash_cl_miner::configureGPU(
bool ethash_cl_miner::s_allowCPU = false;
unsigned ethash_cl_miner::s_extraRequiredGPUMem;
unsigned ethash_cl_miner::s_msPerBatch = Ethash::defaultMSPerBatch;
unsigned ethash_cl_miner::s_workgroupSize = Ethash::defaultLocalWorkSize;
unsigned ethash_cl_miner::s_initialGlobalWorkSize = Ethash::defaultGlobalWorkSizeMultiplier * Ethash::defaultLocalWorkSize;
bool ethash_cl_miner::searchForAllDevices(function<bool(cl::Device const&)> _callback)
{
@ -254,7 +265,6 @@ void ethash_cl_miner::finish()
bool ethash_cl_miner::init(
uint8_t const* _dag,
uint64_t _dagSize,
unsigned _workgroupSize,
unsigned _platformId,
unsigned _deviceId
)
@ -299,14 +309,18 @@ bool ethash_cl_miner::init(
m_context = cl::Context(vector<cl::Device>(&device, &device + 1));
m_queue = cl::CommandQueue(m_context, device);
// use requested workgroup size, but we require multiple of 8
m_workgroupSize = ((_workgroupSize + 7) / 8) * 8;
// make sure that global work size is evenly divisible by the local workgroup size
m_globalWorkSize = s_initialGlobalWorkSize;
if (m_globalWorkSize % s_workgroupSize != 0)
m_globalWorkSize = ((m_globalWorkSize / s_workgroupSize) + 1) * s_workgroupSize;
// remember the device's address bits
m_deviceBits = device.getInfo<CL_DEVICE_ADDRESS_BITS>();
// patch source code
// note: ETHASH_CL_MINER_KERNEL is simply ethash_cl_miner_kernel.cl compiled
// into a byte array by bin2h.cmake. There is no need to load the file by hand in runtime
string code(ETHASH_CL_MINER_KERNEL, ETHASH_CL_MINER_KERNEL + ETHASH_CL_MINER_KERNEL_SIZE);
addDefinition(code, "GROUP_SIZE", m_workgroupSize);
addDefinition(code, "GROUP_SIZE", s_workgroupSize);
addDefinition(code, "DAG_SIZE", (unsigned)(_dagSize / ETHASH_MIX_BYTES));
addDefinition(code, "ACCESSES", ETHASH_ACCESSES);
addDefinition(code, "MAX_OUTPUTS", c_maxSearchResults);
@ -323,7 +337,7 @@ bool ethash_cl_miner::init(
ETHCL_LOG("Printing program log");
ETHCL_LOG(program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(device).c_str());
}
catch (cl::Error const& err)
catch (cl::Error const&)
{
ETHCL_LOG(program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(device).c_str());
return false;
@ -415,9 +429,8 @@ bool ethash_cl_miner::init(
return true;
}
void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook& hook, unsigned _msPerBatch)
void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook& hook)
{
(void)_msPerBatch;
try
{
struct pending_batch
@ -454,10 +467,9 @@ void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook
unsigned buf = 0;
random_device engine;
uint64_t start_nonce = uniform_int_distribution<uint64_t>()(engine);
for (;; start_nonce += m_batchSize)
for (;; start_nonce += m_globalWorkSize)
{
// chrono::high_resolution_clock::time_point t = chrono::high_resolution_clock::now();
auto t = chrono::high_resolution_clock::now();
// supply output buffer to kernel
m_searchKernel.setArg(0, m_searchBuffer[buf]);
if (m_dagChunksCount == 1)
@ -466,7 +478,7 @@ void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook
m_searchKernel.setArg(6, start_nonce);
// execute it!
m_queue.enqueueNDRangeKernel(m_searchKernel, cl::NullRange, m_batchSize, m_workgroupSize);
m_queue.enqueueNDRangeKernel(m_searchKernel, cl::NullRange, m_globalWorkSize, s_workgroupSize);
pending.push({ start_nonce, buf });
buf = (buf + 1) % c_bufferCount;
@ -486,7 +498,7 @@ void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook
m_queue.enqueueUnmapMemObject(m_searchBuffer[batch.buf], results);
bool exit = num_found && hook.found(nonces, num_found);
exit |= hook.searched(batch.start_nonce, m_batchSize); // always report searched before exit
exit |= hook.searched(batch.start_nonce, m_globalWorkSize); // always report searched before exit
if (exit)
break;
@ -497,19 +509,31 @@ void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook
pending.pop();
}
/* chrono::high_resolution_clock::duration d = chrono::high_resolution_clock::now() - t;
if (d > chrono::milliseconds(_msPerBatch * 10 / 9))
// adjust global work size depending on last search time
if (s_msPerBatch)
{
cerr << "Batch of" << m_batchSize << "took" << chrono::duration_cast<chrono::milliseconds>(d).count() << "ms, >>" << _msPerBatch << "ms.";
m_batchSize = max<unsigned>(128, m_batchSize * 9 / 10);
cerr << "New batch size" << m_batchSize;
// Global work size must be:
// - less than or equal to 2 ^ DEVICE_BITS - 1
// - divisible by lobal work size (workgroup size)
auto d = chrono::duration_cast<chrono::milliseconds>(chrono::high_resolution_clock::now() - t);
if (d != chrono::milliseconds(0)) // if duration is zero, we did not get in the actual searh/or search not finished
{
if (d > chrono::milliseconds(s_msPerBatch * 10 / 9))
{
// cerr << "Batch of " << m_globalWorkSize << " took " << chrono::duration_cast<chrono::milliseconds>(d).count() << " ms, >> " << _msPerBatch << " ms." << endl;
m_globalWorkSize = max<unsigned>(128, m_globalWorkSize + s_workgroupSize);
// cerr << "New global work size" << m_globalWorkSize << endl;
}
else if (d < chrono::milliseconds(s_msPerBatch * 9 / 10))
{
// cerr << "Batch of " << m_globalWorkSize << " took " << chrono::duration_cast<chrono::milliseconds>(d).count() << " ms, << " << _msPerBatch << " ms." << endl;
m_globalWorkSize = min<unsigned>(pow(2, m_deviceBits) - 1, m_globalWorkSize - s_workgroupSize);
// Global work size should never be less than the workgroup size
m_globalWorkSize = max<unsigned>(s_workgroupSize, m_globalWorkSize);
// cerr << "New global work size" << m_globalWorkSize << endl;
}
}
}
else if (d < chrono::milliseconds(_msPerBatch * 9 / 10))
{
cerr << "Batch of" << m_batchSize << "took" << chrono::duration_cast<chrono::milliseconds>(d).count() << "ms, <<" << _msPerBatch << "ms.";
m_batchSize = m_batchSize * 10 / 9;
cerr << "New batch size" << m_batchSize;
}*/
}
// not safe to return until this is ready

16
libethash-cl/ethash_cl_miner.h

@ -45,6 +45,9 @@ public:
static void listDevices();
static bool configureGPU(
unsigned _platformId,
unsigned _localWorkSize,
unsigned _globalWorkSize,
unsigned _msPerBatch,
bool _allowCPU,
unsigned _extraGPUMemory,
boost::optional<uint64_t> _currentBlock
@ -53,12 +56,11 @@ public:
bool init(
uint8_t const* _dag,
uint64_t _dagSize,
unsigned _workgroupSize = 64,
unsigned _platformId = 0,
unsigned _deviceId = 0
);
void finish();
void search(uint8_t const* _header, uint64_t _target, search_hook& _hook, unsigned _msPerBatch = 100);
void search(uint8_t const* _header, uint64_t _target, search_hook& _hook);
void hash_chunk(uint8_t* _ret, uint8_t const* _header, uint64_t _nonce, unsigned _count);
void search_chunk(uint8_t const*_header, uint64_t _target, search_hook& _hook);
@ -76,10 +78,16 @@ private:
cl::Buffer m_header;
cl::Buffer m_hashBuffer[c_bufferCount];
cl::Buffer m_searchBuffer[c_bufferCount];
unsigned m_workgroupSize;
unsigned m_batchSize = c_searchBatchSize;
unsigned m_globalWorkSize;
bool m_openclOnePointOne;
unsigned m_deviceBits;
/// The local work size for the search
static unsigned s_workgroupSize;
/// The initial global work size for the searches
static unsigned s_initialGlobalWorkSize;
/// The target milliseconds per batch for the search. If 0, then no adjustment will happen
static unsigned s_msPerBatch;
/// Allow CPU to appear as an OpenCL device or not. Default is false
static bool s_allowCPU;
/// GPU memory required for other things, like window rendering e.t.c.

16
libethcore/Common.h

@ -97,10 +97,13 @@ enum class RelativeBlock: BlockNumber
Pending = PendingBlock
};
class Transaction;
struct ImportRoute
{
h256s deadBlocks;
h256s liveBlocks;
std::vector<Transaction> goodTranactions;
};
enum class ImportResult
@ -129,10 +132,10 @@ struct ImportRequirements
};
/// Super-duper signal mechanism. TODO: replace with somthing a bit heavier weight.
class Signal
template<typename... Args> class Signal
{
public:
using Callback = std::function<void()>;
using Callback = std::function<void(Args...)>;
class HandlerAux
{
@ -141,7 +144,7 @@ public:
public:
~HandlerAux() { if (m_s) m_s->m_fire.erase(m_i); m_s = nullptr; }
void reset() { m_s = nullptr; }
void fire() { m_h(); }
void fire(Args&&... _args) { m_h(std::forward<Args>(_args)...); }
private:
HandlerAux(unsigned _i, Signal* _s, Callback const& _h): m_i(_i), m_s(_s), m_h(_h) {}
@ -165,13 +168,13 @@ public:
return h;
}
void operator()() { for (auto const& f: m_fire) f.second->fire(); }
void operator()(Args&... _args) { for (auto const& f: m_fire) f.second->fire(std::forward<Args>(_args)...); }
private:
std::map<unsigned, std::shared_ptr<Signal::HandlerAux>> m_fire;
std::map<unsigned, std::shared_ptr<typename Signal::HandlerAux>> m_fire;
};
using Handler = std::shared_ptr<Signal::HandlerAux>;
template<class... Args> using Handler = std::shared_ptr<typename Signal<Args...>::HandlerAux>;
struct TransactionSkeleton
{
@ -182,6 +185,7 @@ struct TransactionSkeleton
bytes data;
u256 gas = UndefinedU256;
u256 gasPrice = UndefinedU256;
u256 nonce = UndefinedU256;
};
void badBlock(bytesConstRef _header, std::string const& _err);

30
libethcore/Ethash.cpp

@ -54,6 +54,9 @@ namespace dev
namespace eth
{
const unsigned Ethash::defaultLocalWorkSize = 64;
const unsigned Ethash::defaultGlobalWorkSizeMultiplier = 512; // * CL_DEFAULT_LOCAL_WORK_SIZE
const unsigned Ethash::defaultMSPerBatch = 100;
const Ethash::WorkPackage Ethash::NullWorkPackage = Ethash::WorkPackage();
std::string Ethash::name()
@ -373,7 +376,7 @@ void Ethash::GPUMiner::workLoop()
this_thread::sleep_for(chrono::milliseconds(500));
}
bytesConstRef dagData = dag->data();
m_miner->init(dagData.data(), dagData.size(), 32, s_platformId, device);
m_miner->init(dagData.data(), dagData.size(), s_platformId, device);
}
uint64_t upper64OfBoundary = (uint64_t)(u64)((u256)w.boundary >> 192);
@ -409,6 +412,9 @@ void Ethash::GPUMiner::listDevices()
}
bool Ethash::GPUMiner::configureGPU(
unsigned _localWorkSize,
unsigned _globalWorkSizeMultiplier,
unsigned _msPerBatch,
unsigned _platformId,
unsigned _deviceId,
bool _allowCPU,
@ -418,7 +424,27 @@ bool Ethash::GPUMiner::configureGPU(
{
s_platformId = _platformId;
s_deviceId = _deviceId;
return ethash_cl_miner::configureGPU(_platformId, _allowCPU, _extraGPUMemory, _currentBlock);
if (_localWorkSize != 32 && _localWorkSize != 64 && _localWorkSize != 128)
{
cout << "Given localWorkSize of " << toString(_localWorkSize) << "is invalid. Must be either 32,64, or 128" << endl;
return false;
}
if (!ethash_cl_miner::configureGPU(
_platformId,
_localWorkSize,
_globalWorkSizeMultiplier * _localWorkSize,
_msPerBatch,
_allowCPU,
_extraGPUMemory,
_currentBlock)
)
{
cout << "No GPU device with sufficient memory was found. Can't GPU mine. Remove the -G argument" << endl;
return false;
}
return true;
}
#endif

11
libethcore/Ethash.h

@ -88,7 +88,7 @@ public:
static unsigned instances() { return s_numInstances > 0 ? s_numInstances : std::thread::hardware_concurrency(); }
static std::string platformInfo();
static void listDevices() {}
static bool configureGPU(unsigned, unsigned, bool, unsigned, boost::optional<uint64_t>) { return false; }
static bool configureGPU(unsigned, unsigned, unsigned, unsigned, unsigned, bool, unsigned, boost::optional<uint64_t>) { return false; }
static void setNumInstances(unsigned _instances) { s_numInstances = std::min<unsigned>(_instances, std::thread::hardware_concurrency()); }
protected:
void kickOff() override
@ -118,6 +118,9 @@ public:
static unsigned getNumDevices();
static void listDevices();
static bool configureGPU(
unsigned _localWorkSize,
unsigned _globalWorkSizeMultiplier,
unsigned _msPerBatch,
unsigned _platformId,
unsigned _deviceId,
bool _allowCPU,
@ -147,6 +150,12 @@ public:
#else
using GPUMiner = CPUMiner;
#endif
/// Default value of the local work size. Also known as workgroup size.
static const unsigned defaultLocalWorkSize;
/// Default value of the global work size as a multiplier of the local work size
static const unsigned defaultGlobalWorkSizeMultiplier;
/// Default value of the milliseconds per global work size (per batch)
static const unsigned defaultMSPerBatch;
};
}

14
libethcore/Transaction.cpp

@ -29,6 +29,20 @@ using namespace std;
using namespace dev;
using namespace dev::eth;
TransactionBase::TransactionBase(TransactionSkeleton const& _ts, Secret const& _s):
m_type(_ts.creation ? ContractCreation : MessageCall),
m_nonce(_ts.nonce),
m_value(_ts.value),
m_receiveAddress(_ts.to),
m_gasPrice(_ts.gasPrice),
m_gas(_ts.gas),
m_data(_ts.data),
m_sender(_ts.from)
{
if (_s)
sign(_s);
}
TransactionBase::TransactionBase(bytesConstRef _rlpData, CheckTransaction _checkSig)
{
int field = 0;

4
libethcore/Transaction.h

@ -51,6 +51,9 @@ public:
/// Constructs a null transaction.
TransactionBase() {}
/// Constructs a transaction from a transaction skeleton & optional secret.
TransactionBase(TransactionSkeleton const& _ts, Secret const& _s = Secret());
/// Constructs a signed message-call transaction.
TransactionBase(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, Address const& _dest, bytes const& _data, u256 const& _nonce, Secret const& _secret): m_type(MessageCall), m_nonce(_nonce), m_value(_value), m_receiveAddress(_dest), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) { sign(_secret); }
@ -69,7 +72,6 @@ public:
/// Constructs a transaction from the given RLP.
explicit TransactionBase(bytes const& _rlp, CheckTransaction _checkSig): TransactionBase(&_rlp, _checkSig) {}
/// Checks equality of transactions.
bool operator==(TransactionBase const& _c) const { return m_type == _c.m_type && (m_type == ContractCreation || m_receiveAddress == _c.m_receiveAddress) && m_value == _c.m_value && m_data == _c.m_data; }
/// Checks inequality of transactions.

4
libethereum/Account.h

@ -152,8 +152,7 @@ public:
h256 codeHash() const { assert(!isFreshCode()); return m_codeHash; }
/// Sets the code of the account. Must only be called when isFreshCode() returns true.
void setCode(bytes&& _code) { assert(isFreshCode()); m_codeCache = _code; changed(); }
void setCode(bytes const& _code) { assert(isFreshCode()); m_codeCache = _code; changed(); }
void setCode(bytes&& _code) { assert(isFreshCode()); m_codeCache = std::move(_code); changed(); }
/// @returns true if the account's code is available through code().
bool codeCacheValid() const { return m_codeHash == EmptySHA3 || m_codeHash == c_contractConceptionCodeHash || m_codeCache.size(); }
@ -206,4 +205,3 @@ private:
}
}

8
libethereum/BlockChain.cpp

@ -337,6 +337,7 @@ tuple<ImportRoute, bool, unsigned> BlockChain::sync(BlockQueue& _bq, OverlayDB c
h256s fresh;
h256s dead;
h256s badBlocks;
Transactions goodTransactions;
unsigned count = 0;
for (VerifiedBlock const& block: blocks)
if (!badBlocks.empty())
@ -351,6 +352,7 @@ tuple<ImportRoute, bool, unsigned> BlockChain::sync(BlockQueue& _bq, OverlayDB c
r = import(block.verified, _stateDB, ImportRequirements::Default & ~ImportRequirements::ValidNonce & ~ImportRequirements::CheckUncles);
fresh += r.liveBlocks;
dead += r.deadBlocks;
goodTransactions += r.goodTranactions;
++count;
}
catch (dev::eth::UnknownParent)
@ -377,7 +379,7 @@ tuple<ImportRoute, bool, unsigned> BlockChain::sync(BlockQueue& _bq, OverlayDB c
badBlocks.push_back(block.verified.info.hash());
}
}
return make_tuple(ImportRoute{dead, fresh}, _bq.doneDrain(badBlocks), count);
return make_tuple(ImportRoute{dead, fresh, goodTransactions}, _bq.doneDrain(badBlocks), count);
}
pair<ImportResult, ImportRoute> BlockChain::attemptImport(bytes const& _block, OverlayDB const& _stateDB, ImportRequirements::value _ir) noexcept
@ -497,6 +499,7 @@ ImportRoute BlockChain::import(VerifiedBlockRef const& _block, OverlayDB const&
BlockReceipts br;
u256 td;
Transactions goodTransactions;
#if ETH_CATCH
try
#endif
@ -510,6 +513,7 @@ ImportRoute BlockChain::import(VerifiedBlockRef const& _block, OverlayDB const&
{
blb.blooms.push_back(s.receipt(i).bloom());
br.receipts.push_back(s.receipt(i));
goodTransactions.push_back(s.pending()[i]);
}
s.cleanup(true);
@ -750,7 +754,7 @@ ImportRoute BlockChain::import(VerifiedBlockRef const& _block, OverlayDB const&
dead.push_back(h);
else
fresh.push_back(h);
return ImportRoute{dead, fresh};
return ImportRoute{dead, fresh, move(goodTransactions)};
}
void BlockChain::clearBlockBlooms(unsigned _begin, unsigned _end)

11
libethereum/BlockChainSync.cpp

@ -41,6 +41,17 @@ using namespace p2p;
unsigned const c_chainReorgSize = 30000; /// Added to estimated hashes to account for potential chain reorganiation
unsigned const c_hashSubchainSize = 8192; /// PV61 subchain size
std::ostream& dev::eth::operator<<(std::ostream& _out, SyncStatus const& _sync)
{
_out << "protocol: " << _sync.protocolVersion << endl;
_out << "state: " << EthereumHost::stateName(_sync.state) << " ";
if (_sync.state == SyncState::Hashes)
_out << _sync.hashesReceived << "/" << (_sync.hashesEstimated ? "~" : "") << _sync.hashesTotal;
if (_sync.state == SyncState::Blocks || _sync.state == SyncState::NewBlocks)
_out << _sync.blocksReceived << "/" << _sync.blocksTotal;
return _out;
}
BlockChainSync::BlockChainSync(EthereumHost& _host):
m_host(_host)
{

5
libethereum/BlockChainSync.h

@ -114,7 +114,7 @@ protected:
void requestBlocks(std::shared_ptr<EthereumPeer> _peer);
protected:
Handler m_bqRoomAvailable; ///< Triggered once block queue
Handler<> m_bqRoomAvailable; ///< Triggered once block queue
mutable RecursiveMutex x_sync;
SyncState m_state = SyncState::Idle; ///< Current sync state
unsigned m_estimatedHashes = 0; ///< Number of estimated hashes for the last peer over PV60. Used for status reporting only.
@ -316,5 +316,8 @@ private:
unsigned m_syncingBlockNumber = 0; ///< Current subchain marker
bool m_hashScanComplete = false; ///< True if leading peer completed hashchain scan and we have a list of subchains ready
};
std::ostream& operator<<(std::ostream& _out, SyncStatus const& _sync);
}
}

8
libethereum/BlockQueue.h

@ -111,8 +111,8 @@ public:
/// Get some infomration on the given block's status regarding us.
QueueStatus blockStatus(h256 const& _h) const;
template <class T> Handler onReady(T const& _t) { return m_onReady.add(_t); }
template <class T> Handler onRoomAvailable(T const& _t) { return m_onRoomAvailable.add(_t); }
template <class T> 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; }
@ -145,8 +145,8 @@ private:
std::unordered_multimap<h256, std::pair<h256, bytes>> m_unknown; ///< For blocks that have an unknown parent; we map their parent hash to the block stuff, and insert once the block appears.
h256Hash m_knownBad; ///< Set of blocks that we know will never be valid.
std::multimap<unsigned, std::pair<h256, bytes>> m_future; ///< Set of blocks that are not yet valid. Ordered by timestamp
Signal m_onReady; ///< Called when a subsequent call to import blocks will return a non-empty container. Be nice and exit fast.
Signal m_onRoomAvailable; ///< Called when space for new blocks becomes availabe after a drain. Be nice and exit fast.
Signal<> m_onReady; ///< Called when a subsequent call to import blocks will return a non-empty container. Be nice and exit fast.
Signal<> m_onRoomAvailable; ///< Called when space for new blocks becomes availabe after a drain. Be nice and exit fast.
mutable Mutex m_verification; ///< Mutex that allows writing to m_verified, m_verifying and m_unverified.
std::condition_variable m_moreToVerify; ///< Signaled when m_unverified has a new entry.

15
libethereum/Client.cpp

@ -604,19 +604,18 @@ void Client::onChainChanged(ImportRoute const& _ir)
for (auto const& t: m_bc.transactions(h))
{
clog(ClientTrace) << "Resubmitting dead-block transaction " << Transaction(t, CheckTransaction::None);
m_tq.import(t, TransactionQueue::ImportCallback(), IfDropped::Retry);
m_tq.import(t, IfDropped::Retry);
}
}
// remove transactions from m_tq nicely rather than relying on out of date nonce later on.
for (auto const& h: _ir.liveBlocks)
{
clog(ClientTrace) << "Live block:" << h;
for (auto const& th: m_bc.transactionHashes(h))
{
clog(ClientTrace) << "Safely dropping transaction " << th;
m_tq.drop(th);
}
for (auto const& t: _ir.goodTranactions)
{
clog(ClientTrace) << "Safely dropping transaction " << t.sha3();
m_tq.dropGood(t);
}
if (auto h = m_host.lock())
@ -651,7 +650,7 @@ void Client::onChainChanged(ImportRoute const& _ir)
for (auto const& t: m_postMine.pending())
{
clog(ClientTrace) << "Resubmitting post-mine transaction " << t;
auto ir = m_tq.import(t, TransactionQueue::ImportCallback(), IfDropped::Retry);
auto ir = m_tq.import(t, IfDropped::Retry);
if (ir != ImportResult::Success)
onTransactionQueueReady();
}

6
libethereum/Client.h

@ -310,10 +310,10 @@ private:
GenericFarm<ProofOfWork> m_farm; ///< Our mining farm.
Handler m_tqReady;
Handler m_bqReady;
Handler<> m_tqReady;
Handler<> m_bqReady;
bool m_wouldMine = false; ///< True if we /should/ be mining.
bool m_wouldMine = false; ///< True if we /should/ be mining.
bool m_turboMining = false; ///< Don't squander all of our time mining actually just sleeping.
bool m_forceMining = false; ///< Mine even when there are no transactions pending?
bool m_mineOnBadChain = false; ///< Mine even when the canary says it's a bad chain.

43
libethereum/ClientBase.cpp

@ -45,48 +45,21 @@ State ClientBase::asOf(BlockNumber _h) const
return asOf(bc().numberHash(_h));
}
void ClientBase::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, u256 _nonce)
{
prepareForTransaction();
Transaction t(_value, _gasPrice, _gas, _dest, _data, _nonce, _secret);
m_tq.import(t.rlp());
StructuredLogger::transactionReceived(t.sha3().abridged(), t.sender().abridged());
cnote << "New transaction " << t;
}
Address ClientBase::submitTransaction(Secret _secret, u256 _value, bytes const& _data, u256 _gas, u256 _gasPrice, u256 _nonce)
{
prepareForTransaction();
Transaction t(_value, _gasPrice, _gas, _data, _nonce, _secret);
m_tq.import(t.rlp());
StructuredLogger::transactionReceived(t.sha3().abridged(), t.sender().abridged());
cnote << "New transaction " << t;
return right160(sha3(rlpList(t.sender(), t.nonce())));
}
void ClientBase::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice)
{
auto a = toAddress(_secret);
submitTransaction(_secret, _value, _dest, _data, _gas, _gasPrice, max<u256>(postMine().transactionsFrom(a), m_tq.maxNonce(a)));
}
Address ClientBase::submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice)
Address ClientBase::submitTransaction(TransactionSkeleton const& _t, Secret const& _secret)
{
prepareForTransaction();
u256 n = postMine().transactionsFrom(toAddress(_secret));
Transaction t(_endowment, _gasPrice, _gas, _init, n, _secret);
m_tq.import(t.rlp());
TransactionSkeleton ts(_t);
ts.from = toAddress(_secret);
if (_t.nonce == UndefinedU256)
ts.nonce = max<u256>(postMine().transactionsFrom(ts.from), m_tq.maxNonce(ts.from));
Transaction t(ts, _secret);
m_tq.import(t.rlp());
StructuredLogger::transactionReceived(t.sha3().abridged(), t.sender().abridged());
cnote << "New transaction " << t;
return right160(sha3(rlpList(t.sender(), t.nonce())));
return _t.creation ? right160(sha3(rlpList(ts.from, ts.nonce))) : Address();
}
// TODO: remove try/catch, allow exceptions

9
libethereum/ClientBase.h

@ -75,14 +75,9 @@ public:
ClientBase() {}
virtual ~ClientBase() {}
/// Submits the given message-call transaction.
virtual void submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, u256 _nonce);
virtual void submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo) override;
/// Submits a new contract-creation transaction.
/// Submits the given transaction.
/// @returns the new contract's address (assuming it all goes through).
virtual Address submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice, u256 _nonce);
virtual Address submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas = 10000, u256 _gasPrice = 10 * szabo) override;
virtual Address submitTransaction(TransactionSkeleton const& _t, Secret const& _secret) override;
using Interface::submitTransaction;
/// Makes the given call. Nothing is recorded into the state.

79
libethereum/EthereumHost.cpp

@ -54,6 +54,7 @@ EthereumHost::EthereumHost(BlockChain const& _ch, TransactionQueue& _tq, BlockQu
m_networkId (_networkId)
{
m_latestBlockSent = _ch.currentHash();
m_tq.onImport([this](ImportResult _ir, h256 const& _h, h512 const& _nodeId) { onTransactionImported(_ir, _h, _nodeId); });
}
EthereumHost::~EthereumHost()
@ -68,6 +69,7 @@ bool EthereumHost::ensureInitialised()
m_latestBlockSent = m_chain.currentHash();
clog(NetNote) << "Initialising: latest=" << m_latestBlockSent;
Guard l(x_transactions);
m_transactionsSent = m_tq.knownTransactions();
return true;
}
@ -82,6 +84,7 @@ void EthereumHost::reset()
m_sync.reset();
m_latestBlockSent = h256();
Guard tl(x_transactions);
m_transactionsSent.clear();
}
@ -116,16 +119,19 @@ void EthereumHost::maintainTransactions()
// Send any new transactions.
unordered_map<std::shared_ptr<EthereumPeer>, std::vector<size_t>> peerTransactions;
auto ts = m_tq.topTransactions(c_maxSendTransactions);
for (size_t i = 0; i < ts.size(); ++i)
{
auto const& t = ts[i];
bool unsent = !m_transactionsSent.count(t.sha3());
auto peers = get<1>(randomSelection(0, [&](EthereumPeer* p) { return p->m_requireTransactions || (unsent && !p->m_knownTransactions.count(t.sha3())); }));
for (auto const& p: peers)
peerTransactions[p].push_back(i);
Guard l(x_transactions);
for (size_t i = 0; i < ts.size(); ++i)
{
auto const& t = ts[i];
bool unsent = !m_transactionsSent.count(t.sha3());
auto peers = get<1>(randomSelection(0, [&](EthereumPeer* p) { return p->m_requireTransactions || (unsent && !p->m_knownTransactions.count(t.sha3())); }));
for (auto const& p: peers)
peerTransactions[p].push_back(i);
}
for (auto const& t: ts)
m_transactionsSent.insert(t.sha3());
}
for (auto const& t: ts)
m_transactionsSent.insert(t.sha3());
foreachPeer([&](shared_ptr<EthereumPeer> _p)
{
bytes b;
@ -291,28 +297,7 @@ void EthereumHost::onPeerTransactions(std::shared_ptr<EthereumPeer> _peer, RLP c
}
unsigned itemCount = _r.itemCount();
clog(NetAllDetail) << "Transactions (" << dec << itemCount << "entries)";
Guard l(_peer->x_knownTransactions);
for (unsigned i = 0; i < min<unsigned>(itemCount, 32); ++i) // process 256 transactions at most. TODO: much better solution.
{
auto h = sha3(_r[i].data());
_peer->m_knownTransactions.insert(h);
ImportResult ir = m_tq.import(_r[i].data());
switch (ir)
{
case ImportResult::Malformed:
_peer->addRating(-100);
break;
case ImportResult::AlreadyKnown:
// if we already had the transaction, then don't bother sending it on.
m_transactionsSent.insert(h);
_peer->addRating(0);
break;
case ImportResult::Success:
_peer->addRating(100);
break;
default:;
}
}
m_tq.enqueue(_r, _peer->session()->id());
}
void EthereumHost::onPeerAborting()
@ -344,3 +329,37 @@ SyncStatus EthereumHost::status() const
return SyncStatus();
return m_sync->status();
}
void EthereumHost::onTransactionImported(ImportResult _ir, h256 const& _h, h512 const& _nodeId)
{
auto session = host()->peerSession(_nodeId);
if (!session)
return;
std::shared_ptr<EthereumPeer> peer = session->cap<EthereumPeer>();
if (!peer)
peer = session->cap<EthereumPeer>(c_oldProtocolVersion);
if (!peer)
return;
Guard l(peer->x_knownTransactions);
peer->m_knownTransactions.insert(_h);
switch (_ir)
{
case ImportResult::Malformed:
peer->addRating(-100);
break;
case ImportResult::AlreadyKnown:
// if we already had the transaction, then don't bother sending it on.
{
Guard l(x_transactions);
m_transactionsSent.insert(_h);
}
peer->addRating(0);
break;
case ImportResult::Success:
peer->addRating(100);
break;
default:;
}
}

2
libethereum/EthereumHost.h

@ -105,6 +105,7 @@ private:
void maintainTransactions();
void maintainBlocks(h256 const& _currentBlock);
void onTransactionImported(ImportResult _ir, h256 const& _h, h512 const& _nodeId);
/// Check to see if the network peer-state initialisation has happened.
bool isInitialised() const { return (bool)m_latestBlockSent; }
@ -132,6 +133,7 @@ private:
bool m_newBlocks = false;
mutable Mutex x_sync;
mutable Mutex x_transactions;
DownloadMan m_man;
std::unique_ptr<BlockChainSync> m_sync;
};

9
libethereum/Executive.cpp

@ -153,6 +153,11 @@ u256 Executive::gasUsed() const
return m_t.gas() - m_gas;
}
u256 Executive::gasUsedNoRefunds() const
{
return m_t.gas() - m_gas + m_refunded;
}
void Executive::accrueSubState(SubState& _parentContext)
{
if (m_ext)
@ -391,8 +396,8 @@ void Executive::finalize()
// SSTORE refunds...
// must be done before the miner gets the fees.
if (m_ext)
m_gas += min((m_t.gas() - m_gas) / 2, m_ext->sub.refunds);
m_refunded = m_ext ? min((m_t.gas() - m_gas) / 2, m_ext->sub.refunds) : 0;
m_gas += m_refunded;
// cnote << "Refunding" << formatBalance(m_endGas * m_ext->gasPrice) << "to origin (=" << m_endGas << "*" << formatBalance(m_ext->gasPrice) << ")";
m_s.addBalance(m_t.sender(), m_gas * m_t.gasPrice());

4
libethereum/Executive.h

@ -110,6 +110,9 @@ public:
/// @returns total gas used in the transaction/operation.
/// @warning Only valid after finalise().
u256 gasUsed() const;
/// @returns total gas used in the transaction/operation, excluding anything refunded.
/// @warning Only valid after finalise().
u256 gasUsedNoRefunds() const;
/// Set up the executive for evaluating a bare CREATE (contract-creation) operation.
/// @returns false iff go() must be called (and thus a VM execution in required).
@ -154,6 +157,7 @@ private:
bool m_isCreation = false; ///< True if the transaction creates a contract, or if create() is called.
TransactionException m_excepted = TransactionException::None; ///< Details if the VM's execution resulted in an exception.
u256 m_gas = 0; ///< The gas for EVM code execution. Initial amount before go() execution, final amount after go() execution.
u256 m_refunded = 0; ///< The amount of gas refunded.
Transaction m_t; ///< The original transaction. Set by setup().
LogEntries m_logs; ///< The log entries created by this transaction. Set by finalize().

2
libethereum/ExtVM.cpp

@ -75,9 +75,11 @@ void go(unsigned _depth, Executive& _e, OnOpFunc const& _onOp)
// Current stack is too small to handle more CALL/CREATE executions.
// It needs to be done only once as newly allocated stack space it enough to handle
// the rest of the calls up to the depth limit (c_depthLimit).
#if __GNUC__
if (_depth == c_offloadPoint)
goOnOffloadedStack(_e, _onOp);
else
#endif
_e.go(_onOp);
}
}

29
libethereum/Interface.cpp

@ -20,6 +20,31 @@
*/
#include "Interface.h"
using namespace std;
using namespace dev;
using namespace eth;
#pragma GCC diagnostic ignored "-Wunused-variable"
namespace { char dummy; }
void Interface::submitTransaction(Secret const& _secret, u256 const& _value, Address const& _dest, bytes const& _data, u256 const& _gas, u256 const& _gasPrice, u256 const& _nonce)
{
TransactionSkeleton ts;
ts.creation = false;
ts.value = _value;
ts.to = _dest;
ts.data = _data;
ts.gas = _gas;
ts.gasPrice = _gasPrice;
ts.nonce = _nonce;
submitTransaction(ts, _secret);
}
Address Interface::submitTransaction(Secret const& _secret, u256 const& _endowment, bytes const& _init, u256 const& _gas, u256 const& _gasPrice, u256 const& _nonce)
{
TransactionSkeleton ts;
ts.creation = true;
ts.value = _endowment;
ts.data = _init;
ts.gas = _gas;
ts.gasPrice = _gasPrice;
ts.nonce = _nonce;
return submitTransaction(ts, _secret);
}

12
libethereum/Interface.h

@ -65,16 +65,16 @@ public:
// [TRANSACTION API]
/// Submits the given message-call transaction.
virtual void submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo) = 0;
/// Submits a new transaction.
/// @returns the new contract's address (assuming it all goes through and it's contract creation).
virtual Address submitTransaction(TransactionSkeleton const& _t, Secret const& _secret) = 0;
/// Submits a new contract-creation transaction.
/// @returns the new contract's address (assuming it all goes through).
virtual Address submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas = 10000, u256 _gasPrice = 10 * szabo) = 0;
/// Submits the given message-call transaction.
void submitTransaction(Secret const& _secret, u256 const& _value, Address const& _dest, bytes const& _data = bytes(), u256 const& _gas = 10000, u256 const& _gasPrice = 10 * szabo, u256 const& _nonce = UndefinedU256);
/// Submits a new contract-creation transaction.
/// @returns the new contract's address (assuming it all goes through).
Address submitTransaction(Secret const& _secret, TransactionSkeleton const& _t) { if (_t.creation) return submitTransaction(_secret, _t.value, _t.data, _t.gas, _t.gasPrice); submitTransaction(_secret, _t.value, _t.to, _t.data, _t.gas, _t.gasPrice); return Address(); }
Address submitTransaction(Secret const& _secret, u256 const& _endowment, bytes const& _init, u256 const& _gas = 10000, u256 const& _gasPrice = 10 * szabo, u256 const& _nonce = UndefinedU256);
/// Blocks until all pending transactions have been processed.
virtual void flushTransactions() = 0;

6
libethereum/State.cpp

@ -38,6 +38,7 @@
#include "Executive.h"
#include "CachedAddressState.h"
#include "CanonBlockChain.h"
#include "TransactionQueue.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
@ -535,7 +536,7 @@ pair<TransactionReceipts, bool> State::sync(BlockChain const& _bc, TransactionQu
if (req > got)
{
// too old
for (Transaction const& mt: m_transactions)
/* for (Transaction const& mt: m_transactions)
{
if (mt.from() == t.from())
{
@ -544,7 +545,7 @@ pair<TransactionReceipts, bool> State::sync(BlockChain const& _bc, TransactionQu
else if (mt.nonce() == t.nonce() && mt.gasPrice() <= t.gasPrice())
cnote << t.sha3() << "Dropping old transaction (gas price lower)";
}
}
}*/
_tq.drop(t.sha3());
}
else if (got > req + _tq.waiting(t.sender()))
@ -1268,6 +1269,7 @@ ExecutionResult State::execute(LastHashes const& _lh, Transaction const& _t, Per
// Add to the user-originated transactions that we've executed.
m_transactions.push_back(e.t());
// m_receipts.push_back(TransactionReceipt(rootHash(), startGasUsed + e.gasUsedNoRefunds(), e.logs())); // TODO: Switch in to be compliant with YP.
m_receipts.push_back(TransactionReceipt(rootHash(), startGasUsed + e.gasUsed(), e.logs()));
m_transactionSet.insert(e.t().sha3());
}

2
libethereum/State.h

@ -32,7 +32,6 @@
#include <libethcore/ProofOfWork.h>
#include <libethcore/Miner.h>
#include <libevm/ExtVMFace.h>
#include "TransactionQueue.h"
#include "Account.h"
#include "Transaction.h"
#include "TransactionReceipt.h"
@ -67,6 +66,7 @@ using LogBloomRequirementError = boost::tuple<errinfo_required_LogBloom, errinfo
class BlockChain;
class State;
class TransactionQueue;
struct VerifiedBlockRef;
struct StateChat: public LogChannel { static const char* name(); static const int verbosity = 4; };

7
libethereum/Transaction.h

@ -85,6 +85,9 @@ public:
/// Constructs a null transaction.
Transaction() {}
/// Constructs from a transaction skeleton & optional secret.
Transaction(TransactionSkeleton const& _ts, Secret const& _s = Secret()): TransactionBase(_ts, _s) {}
/// Constructs a signed message-call transaction.
Transaction(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, Address const& _dest, bytes const& _data, u256 const& _nonce, Secret const& _secret):
TransactionBase(_value, _gasPrice, _gas, _dest, _data, _nonce, _secret)
@ -96,12 +99,12 @@ public:
{}
/// Constructs an unsigned message-call transaction.
Transaction(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, Address const& _dest, bytes const& _data, u256 const& _nonce = 0):
Transaction(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, Address const& _dest, bytes const& _data, u256 const& _nonce = UndefinedU256):
TransactionBase(_value, _gasPrice, _gas, _dest, _data, _nonce)
{}
/// Constructs an unsigned contract-creation transaction.
Transaction(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, bytes const& _data, u256 const& _nonce = 0):
Transaction(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, bytes const& _data, u256 const& _nonce = UndefinedU256):
TransactionBase(_value, _gasPrice, _gas, _data, _nonce)
{}

153
libethereum/TransactionQueue.cpp

@ -31,7 +31,28 @@ using namespace dev::eth;
const char* TransactionQueueChannel::name() { return EthCyan "┉┅▶"; }
const char* TransactionQueueTraceChannel::name() { return EthCyan " ┅▶"; }
ImportResult TransactionQueue::import(bytesConstRef _transactionRLP, ImportCallback const& _cb, IfDropped _ik)
TransactionQueue::TransactionQueue(unsigned _limit, unsigned _futureLimit):
m_current(PriorityCompare { *this }),
m_limit(_limit),
m_futureLimit(_futureLimit)
{
unsigned verifierThreads = std::max(thread::hardware_concurrency(), 3U) - 2U;
for (unsigned i = 0; i < verifierThreads; ++i)
m_verifiers.emplace_back([=](){
setThreadName("txcheck" + toString(i));
this->verifierBody();
});
}
TransactionQueue::~TransactionQueue()
{
m_aborting = true;
m_queueReady.notify_all();
for (auto& i: m_verifiers)
i.join();
}
ImportResult TransactionQueue::import(bytesConstRef _transactionRLP, IfDropped _ik)
{
// Check if we already know this transaction.
h256 h = sha3(_transactionRLP);
@ -49,14 +70,13 @@ ImportResult TransactionQueue::import(bytesConstRef _transactionRLP, ImportCallb
{
t = Transaction(_transactionRLP, CheckTransaction::Everything);
UpgradeGuard ul(l);
ir = manageImport_WITH_LOCK(h, t, _cb);
ir = manageImport_WITH_LOCK(h, t);
}
catch (...)
{
return ImportResult::Malformed;
}
}
// cdebug << "import-END: Nonce of" << t.sender() << "now" << maxNonce(t.sender());
return ir;
}
@ -71,27 +91,24 @@ ImportResult TransactionQueue::check_WITH_LOCK(h256 const& _h, IfDropped _ik)
return ImportResult::Success;
}
ImportResult TransactionQueue::import(Transaction const& _transaction, ImportCallback const& _cb, IfDropped _ik)
ImportResult TransactionQueue::import(Transaction const& _transaction, IfDropped _ik)
{
// Check if we already know this transaction.
h256 h = _transaction.sha3(WithSignature);
// cdebug << "import-BEGIN: Nonce of sender" << maxNonce(_transaction.sender());
ImportResult ret;
{
UpgradableGuard l(m_lock);
// TODO: keep old transactions around and check in State for nonce validity
auto ir = check_WITH_LOCK(h, _ik);
if (ir != ImportResult::Success)
return ir;
{
UpgradeGuard ul(l);
ret = manageImport_WITH_LOCK(h, _transaction, _cb);
ret = manageImport_WITH_LOCK(h, _transaction);
}
}
// cdebug << "import-END: Nonce of" << _transaction.sender() << "now" << maxNonce(_transaction.sender());
return ret;
}
@ -111,7 +128,7 @@ h256Hash TransactionQueue::knownTransactions() const
return m_known;
}
ImportResult TransactionQueue::manageImport_WITH_LOCK(h256 const& _h, Transaction const& _transaction, ImportCallback const& _cb)
ImportResult TransactionQueue::manageImport_WITH_LOCK(h256 const& _h, Transaction const& _transaction)
{
try
{
@ -149,10 +166,8 @@ ImportResult TransactionQueue::manageImport_WITH_LOCK(h256 const& _h, Transactio
}
}
}
// If valid, append to blocks.
// If valid, append to transactions.
insertCurrent_WITH_LOCK(make_pair(_h, _transaction));
if (_cb)
m_callbacks[_h] = _cb;
clog(TransactionQueueTraceChannel) << "Queued vaguely legit-looking transaction" << _h;
while (m_current.size() > m_limit)
@ -179,7 +194,6 @@ ImportResult TransactionQueue::manageImport_WITH_LOCK(h256 const& _h, Transactio
u256 TransactionQueue::maxNonce(Address const& _a) const
{
// cdebug << "txQ::maxNonce" << _a;
ReadGuard l(m_lock);
return maxNonce_WITH_LOCK(_a);
}
@ -212,29 +226,7 @@ void TransactionQueue::insertCurrent_WITH_LOCK(std::pair<h256, Transaction> cons
m_currentByHash[_p.first] = handle;
// Move following transactions from future to current
auto fs = m_future.find(t.from());
if (fs != m_future.end())
{
u256 nonce = t.nonce() + 1;
auto fb = fs->second.find(nonce);
if (fb != fs->second.end())
{
auto ft = fb;
while (ft != fs->second.end() && ft->second.transaction.nonce() == nonce)
{
inserted = m_currentByAddressAndNonce[t.from()].insert(std::make_pair(ft->second.transaction.nonce(), PriorityQueue::iterator()));
PriorityQueue::iterator handle = m_current.emplace(move(ft->second));
inserted.first->second = handle;
m_currentByHash[(*handle).transaction.sha3()] = handle;
--m_futureSize;
++ft;
++nonce;
}
fs->second.erase(fb, ft);
if (fs->second.empty())
m_future.erase(t.from());
}
}
makeCurrent_WITH_LOCK(t);
m_known.insert(_p.first);
}
@ -296,6 +288,44 @@ void TransactionQueue::setFuture(h256 const& _txHash)
m_currentByAddressAndNonce.erase(from);
}
void TransactionQueue::makeCurrent_WITH_LOCK(Transaction const& _t)
{
auto fs = m_future.find(_t.from());
if (fs != m_future.end())
{
u256 nonce = _t.nonce() + 1;
auto fb = fs->second.find(nonce);
if (fb != fs->second.end())
{
auto ft = fb;
while (ft != fs->second.end() && ft->second.transaction.nonce() == nonce)
{
auto inserted = m_currentByAddressAndNonce[_t.from()].insert(std::make_pair(ft->second.transaction.nonce(), PriorityQueue::iterator()));
PriorityQueue::iterator handle = m_current.emplace(move(ft->second));
inserted.first->second = handle;
m_currentByHash[(*handle).transaction.sha3()] = handle;
--m_futureSize;
++ft;
++nonce;
}
fs->second.erase(fb, ft);
if (fs->second.empty())
m_future.erase(_t.from());
}
}
while (m_futureSize > m_futureLimit)
{
// TODO: priority queue for future transactions
// For now just drop random chain end
--m_futureSize;
clog(TransactionQueueTraceChannel) << "Dropping out of bounds future transaction" << m_future.begin()->second.rbegin()->second.transaction.sha3();
m_future.begin()->second.erase(--m_future.begin()->second.end());
if (m_future.begin()->second.empty())
m_future.erase(m_future.begin());
}
}
void TransactionQueue::drop(h256 const& _txHash)
{
UpgradableGuard l(m_lock);
@ -305,9 +335,17 @@ void TransactionQueue::drop(h256 const& _txHash)
UpgradeGuard ul(l);
m_dropped.insert(_txHash);
remove_WITH_LOCK(_txHash);
}
void TransactionQueue::dropGood(Transaction const& _t)
{
WriteGuard l(m_lock);
makeCurrent_WITH_LOCK(_t);
if (!m_known.count(_t.sha3()))
return;
m_dropped.insert(_t.sha3());
remove_WITH_LOCK(_t.sha3());
}
void TransactionQueue::clear()
@ -320,3 +358,44 @@ void TransactionQueue::clear()
m_future.clear();
m_futureSize = 0;
}
void TransactionQueue::enqueue(RLP const& _data, h512 const& _nodeId)
{
{
Guard l(x_queue);
unsigned itemCount = _data.itemCount();
for (unsigned i = 0; i < itemCount; ++i)
m_unverified.emplace_back(UnverifiedTransaction(_data[i].data(), _nodeId));
}
m_queueReady.notify_all();
}
void TransactionQueue::verifierBody()
{
while (!m_aborting)
{
UnverifiedTransaction work;
{
unique_lock<Mutex> l(x_queue);
m_queueReady.wait(l, [&](){ return !m_unverified.empty() || m_aborting; });
if (m_aborting)
return;
work = move(m_unverified.front());
m_unverified.pop_front();
}
try
{
Transaction t(work.transaction, CheckTransaction::Cheap); //Signature will be checked later
ImportResult ir = import(t);
m_onImport(ir, t.sha3(), work.nodeId);
}
catch (...)
{
// should not happen as exceptions are handled in import.
cwarn << "Bad transaction:" << boost::current_exception_diagnostic_information();
}
}
}

48
libethereum/TransactionQueue.h

@ -22,6 +22,9 @@
#pragma once
#include <functional>
#include <condition_variable>
#include <thread>
#include <deque>
#include <libdevcore/Common.h>
#include <libdevcore/Guards.h>
#include <libdevcore/Log.h>
@ -49,15 +52,14 @@ enum class IfDropped { Ignore, Retry };
class TransactionQueue
{
public:
using ImportCallback = std::function<void(ImportResult)>;
/// @brief TransactionQueue
/// @param _limit Maximum number of pending transactions in the queue
/// @param _futureLimit Maximum number of future nonce transactions
TransactionQueue(unsigned _limit = 1024, unsigned _futureLimit = 1024): m_current(PriorityCompare { *this }), m_limit(_limit), m_futureLimit(_futureLimit) {}
ImportResult import(Transaction const& _tx, ImportCallback const& _cb = ImportCallback(), IfDropped _ik = IfDropped::Ignore);
ImportResult import(bytes const& _tx, ImportCallback const& _cb = ImportCallback(), IfDropped _ik = IfDropped::Ignore) { return import(&_tx, _cb, _ik); }
ImportResult import(bytesConstRef _tx, ImportCallback const& _cb = ImportCallback(), IfDropped _ik = IfDropped::Ignore);
TransactionQueue(unsigned _limit = 1024, unsigned _futureLimit = 1024);
~TransactionQueue();
void enqueue(RLP const& _data, h512 const& _nodeId);
ImportResult import(bytes const& _tx, IfDropped _ik = IfDropped::Ignore) { return import(&_tx, _ik); }
ImportResult import(Transaction const& _tx, IfDropped _ik = IfDropped::Ignore);
void drop(h256 const& _txHash);
@ -66,9 +68,11 @@ public:
h256Hash knownTransactions() const;
u256 maxNonce(Address const& _a) const;
void setFuture(h256 const& _t);
void dropGood(Transaction const& _t);
void clear();
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<ImportResult, h256 const&, h512 const&> onImport(T const& _t) { return m_onImport.add(_t); }
private:
struct VerifiedTransaction
@ -77,11 +81,25 @@ private:
VerifiedTransaction(VerifiedTransaction&& _t): transaction(std::move(_t.transaction)) {}
VerifiedTransaction(VerifiedTransaction const&) = delete;
VerifiedTransaction operator=(VerifiedTransaction const&) = delete;
VerifiedTransaction& operator=(VerifiedTransaction const&) = delete;
Transaction transaction;
};
struct UnverifiedTransaction
{
UnverifiedTransaction() {}
UnverifiedTransaction(bytesConstRef const& _t, h512 const& _nodeId): transaction(std::move(_t.toBytes())), nodeId(_nodeId) {}
UnverifiedTransaction(UnverifiedTransaction&& _t): transaction(std::move(_t.transaction)) {}
UnverifiedTransaction& operator=(UnverifiedTransaction&& _other) { transaction = std::move(_other.transaction); nodeId = std::move(_other.nodeId); return *this; }
UnverifiedTransaction(UnverifiedTransaction const&) = delete;
UnverifiedTransaction& operator=(UnverifiedTransaction const&) = delete;
bytes transaction;
h512 nodeId;
};
struct PriorityCompare
{
TransactionQueue& queue;
@ -96,12 +114,15 @@ private:
// Use a set with dynamic comparator for minmax priority queue. The comparator takes into account min account nonce. Updating it does not affect the order.
using PriorityQueue = std::multiset<VerifiedTransaction, PriorityCompare>;
ImportResult import(bytesConstRef _tx, IfDropped _ik = IfDropped::Ignore);
ImportResult check_WITH_LOCK(h256 const& _h, IfDropped _ik);
ImportResult manageImport_WITH_LOCK(h256 const& _h, Transaction const& _transaction, ImportCallback const& _cb);
ImportResult manageImport_WITH_LOCK(h256 const& _h, Transaction const& _transaction);
void insertCurrent_WITH_LOCK(std::pair<h256, Transaction> const& _p);
void makeCurrent_WITH_LOCK(Transaction const& _t);
bool remove_WITH_LOCK(h256 const& _txHash);
u256 maxNonce_WITH_LOCK(Address const& _a) const;
void verifierBody();
mutable SharedMutex m_lock; ///< General lock.
h256Hash m_known; ///< Hashes of transactions in both sets.
@ -114,10 +135,17 @@ private:
std::unordered_map<Address, std::map<u256, PriorityQueue::iterator>> m_currentByAddressAndNonce; ///< Transactions grouped by account and nonce
std::unordered_map<Address, std::map<u256, VerifiedTransaction>> m_future; /// Future transactions
Signal m_onReady; ///< Called when a subsequent call to import transactions will return a non-empty container. Be nice and exit fast.
Signal<> m_onReady; ///< Called when a subsequent call to import transactions will return a non-empty container. Be nice and exit fast.
Signal<ImportResult, h256 const&, h512 const&> m_onImport; ///< Called for each import attempt. Arguments are result, transaction id an node id. Be nice and exit fast.
unsigned m_limit; ///< Max number of pending transactions
unsigned m_futureLimit; ///< Max number of future transactions
unsigned m_futureSize = 0; ///< Current number of future transactions
std::condition_variable m_queueReady; ///< Signaled when m_unverified has a new entry.
std::vector<std::thread> m_verifiers;
std::deque<UnverifiedTransaction> m_unverified; ///< Pending verification queue
mutable Mutex x_queue; ///< Verification queue mutex
bool m_aborting = false; ///< Exit condition for verifier.
};
}

2
libethereum/VerifiedBlock.h

@ -47,7 +47,7 @@ struct VerifiedBlock
VerifiedBlock(BlockInfo&& _bi)
{
verified.info = _bi;
verified.info = std::move(_bi);
}
VerifiedBlock(VerifiedBlock&& _other):

6
libevm/ExtVMFace.cpp

@ -21,22 +21,20 @@
#include "ExtVMFace.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
ExtVMFace::ExtVMFace(Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytes const& _code, h256 const& _codeHash, BlockInfo const& _previousBlock, BlockInfo const& _currentBlock, LastHashes const& _lh, unsigned _depth):
ExtVMFace::ExtVMFace(Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytes _code, h256 const& _codeHash, BlockInfo const& _previousBlock, BlockInfo const& _currentBlock, LastHashes const& _lh, unsigned _depth):
myAddress(_myAddress),
caller(_caller),
origin(_origin),
value(_value),
gasPrice(_gasPrice),
data(_data),
code(_code),
code(std::move(_code)),
codeHash(_codeHash),
lastHashes(_lh),
previousBlock(_previousBlock),
currentBlock(_currentBlock),
depth(_depth)
{}

2
libevm/ExtVMFace.h

@ -161,7 +161,7 @@ public:
ExtVMFace() = default;
/// Full constructor.
ExtVMFace(Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytes const& _code, h256 const& _codeHash, BlockInfo const& _previousBlock, BlockInfo const& _currentBlock, LastHashes const& _lh, unsigned _depth);
ExtVMFace(Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytes _code, h256 const& _codeHash, BlockInfo const& _previousBlock, BlockInfo const& _currentBlock, LastHashes const& _lh, unsigned _depth);
virtual ~ExtVMFace() = default;

1
libjsengine/CMakeLists.txt

@ -19,7 +19,6 @@ file(GLOB HEADERS "*.h")
include(EthUtils)
eth_add_resources("${CMAKE_CURRENT_SOURCE_DIR}/JSResources.cmake" "JSRES")
message(STATUS "HERE!!! ${JSRES}")
add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS} ${JSRES})
# macos brew version of v8 needs to be compiled with libstdc++

7
libp2p/Host.h

@ -202,6 +202,9 @@ public:
/// Validates and starts peer session, taking ownership of _io. Disconnects and returns false upon error.
void startPeerSession(Public const& _id, RLP const& _hello, RLPXFrameCoder* _io, std::shared_ptr<RLPXSocket> const& _s);
/// Get session by id
std::shared_ptr<Session> peerSession(NodeId const& _id) { RecursiveGuard l(x_sessions); return m_sessions.count(_id) ? m_sessions[_id].lock() : std::shared_ptr<Session>(); }
protected:
void onNodeTableEvent(NodeId const& _n, NodeTableEventType const& _e);
@ -211,8 +214,8 @@ protected:
private:
enum PeerSlotRatio { Egress = 2, Ingress = 9 };
bool havePeerSession(NodeId _id) { RecursiveGuard l(x_sessions); return m_sessions.count(_id) ? !!m_sessions[_id].lock() : false; }
bool havePeerSession(NodeId const& _id) { return !!peerSession(_id); }
/// Determines and sets m_tcpPublic to publicly advertised address.
void determinePublic();

7
libp2p/Session.cpp

@ -256,9 +256,8 @@ bool Session::checkPacket(bytesConstRef _msg)
void Session::send(bytes&& _msg)
{
clog(NetLeft) << RLP(bytesConstRef(&_msg).cropped(1));
bytesConstRef msg(&_msg);
clog(NetLeft) << RLP(msg.cropped(1));
if (!checkPacket(msg))
clog(NetWarn) << "INVALID PACKET CONSTRUCTED!";
@ -268,7 +267,7 @@ void Session::send(bytes&& _msg)
bool doWrite = false;
{
Guard l(x_writeQueue);
m_writeQueue.push_back(_msg);
m_writeQueue.push_back(std::move(_msg));
doWrite = (m_writeQueue.size() == 1);
}
@ -387,7 +386,7 @@ void Session::doRead()
drop(BadProtocol);
return;
}
/// read padded frame and mac
auto tlen = header.length + header.padding + h128::size;
ba::async_read(m_socket->ref(), boost::asio::buffer(m_data, tlen), [this, self, header, tlen](boost::system::error_code ec, std::size_t length)

10
libtestutils/StateLoader.cpp

@ -1,16 +1,16 @@
/*
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/>.
*/
@ -36,10 +36,10 @@ StateLoader::StateLoader(Json::Value const& _json, std::string const& _dbPath):
Address address = Address(name);
bytes code = fromHex(o["code"].asString().substr(2));
if (code.size())
if (!code.empty())
{
m_state.m_cache[address] = Account(u256(o["balance"].asString()), Account::ContractConception);
m_state.m_cache[address].setCode(code);
m_state.m_cache[address].setCode(std::move(code));
}
else
m_state.m_cache[address] = Account(u256(o["balance"].asString()), Account::NormalCreation);

4
libweb3jsonrpc/AccountHolder.cpp

@ -109,7 +109,7 @@ AddressHash SimpleAccountHolder::realAccounts() const
void SimpleAccountHolder::authenticate(dev::eth::TransactionSkeleton const& _t)
{
if (isRealAccount(_t.from))
m_client()->submitTransaction(m_keyManager.secret(_t.from, [&](){ return m_getPassword(_t.from); }), _t);
m_client()->submitTransaction(_t, m_keyManager.secret(_t.from, [&](){ return m_getPassword(_t.from); }));
else if (isProxyAccount(_t.from))
queueTransaction(_t);
}
@ -117,7 +117,7 @@ void SimpleAccountHolder::authenticate(dev::eth::TransactionSkeleton const& _t)
void FixedAccountHolder::authenticate(dev::eth::TransactionSkeleton const& _t)
{
if (isRealAccount(_t.from))
m_client()->submitTransaction(m_accounts[_t.from], _t);
m_client()->submitTransaction(_t, m_accounts[_t.from]);
else if (isProxyAccount(_t.from))
queueTransaction(_t);
}

3
libweb3jsonrpc/JsonHelper.cpp

@ -269,6 +269,9 @@ TransactionSkeleton toTransactionSkeleton(Json::Value const& _json)
if (!_json["code"].empty())
ret.data = jsToBytes(_json["code"].asString());
if (!_json["nonce"].empty())
ret.nonce = jsToU256(_json["nonce"].asString());
return ret;
}

2
libweb3jsonrpc/WebThreeStubServerBase.cpp

@ -248,7 +248,7 @@ string WebThreeStubServerBase::eth_sendTransaction(Json::Value const& _json)
if (!t.from)
t.from = m_ethAccounts->defaultTransactAccount();
if (t.creation)
ret = toJS(right160(sha3(rlpList(t.from, client()->countAt(t.from)))));;
ret = toJS(right160(sha3(rlpList(t.from, client()->countAt(t.from)))));
if (t.gasPrice == UndefinedU256)
t.gasPrice = 10 * dev::eth::szabo; // TODO: should be determined by user somehow.
if (t.gas == UndefinedU256)

8
mix/DebuggingStateWrapper.h

@ -69,7 +69,7 @@ class QSolState: public QObject
public:
QSolState(QObject* _parent, QVariantMap&& _storage, QVariantList&& _callStack, QVariantMap&& _locals, int _start, int _end, QString _sourceName):
QObject(_parent), m_storage(_storage), m_callStack(_callStack), m_locals(_locals), m_start(_start), m_end(_end), m_sourceName(_sourceName)
QObject(_parent), m_storage(std::move(_storage)), m_callStack(std::move(_callStack)), m_locals(std::move(_locals)), m_start(_start), m_end(_end), m_sourceName(_sourceName)
{ }
private:
@ -92,7 +92,7 @@ class QCode: public QObject
Q_PROPERTY(QString documentId MEMBER m_document CONSTANT)
public:
QCode(QObject* _owner, QString const& _address, QVariantList&& _instrunctions): QObject(_owner), m_instructions(_instrunctions), m_address(_address) {}
QCode(QObject* _owner, QString const& _address, QVariantList&& _instrunctions): QObject(_owner), m_instructions(std::move(_instrunctions)), m_address(_address) {}
void setDocument(QString const& _documentId) { m_document = _documentId; }
private:
@ -110,7 +110,7 @@ class QCallData: public QObject
Q_PROPERTY(QVariantList items MEMBER m_items CONSTANT)
public:
QCallData(QObject* _owner, QVariantList&& _items): QObject(_owner), m_items(_items) {}
QCallData(QObject* _owner, QVariantList&& _items): QObject(_owner), m_items(std::move(_items)) {}
private:
QVariantList m_items;
@ -126,7 +126,7 @@ class QDebugData: public QObject
public:
QDebugData() { }
void setStates(QVariantList&& _states) { m_states = _states; }
void setStates(QVariantList&& _states) { m_states = std::move(_states); }
private:
QVariantList m_states;

2
mix/HttpServer.h

@ -46,7 +46,7 @@ class HttpRequest: public QObject
private:
HttpRequest(QObject* _parent, QUrl&& _url, QString&& _content, QVariantMap&& _headers):
QObject(_parent), m_url(_url), m_content(_content), m_headers(_headers)
QObject(_parent), m_url(std::move(_url)), m_content(std::move(_content)), m_headers(std::move(_headers))
{
}

28
mix/MixClient.cpp

@ -303,22 +303,14 @@ State MixClient::asOf(h256 const& _block) const
return ret;
}
void MixClient::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, bool _gasAuto)
Address MixClient::submitTransaction(eth::TransactionSkeleton const& _ts, Secret const& _secret, bool _gasAuto)
{
WriteGuard l(x_state);
u256 n = m_state.transactionsFrom(toAddress(_secret));
Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret);
TransactionSkeleton ts = _ts;
ts.nonce = m_state.transactionsFrom(toAddress(_secret));
eth::Transaction t(ts, _secret);
executeTransaction(t, m_state, false, _gasAuto, _secret);
}
Address MixClient::submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice, bool _gasAuto)
{
WriteGuard l(x_state);
u256 n = m_state.transactionsFrom(toAddress(_secret));
eth::Transaction t(_endowment, _gasPrice, _gas, _init, n, _secret);
executeTransaction(t, m_state, false, _gasAuto, _secret);
Address address = right160(sha3(rlpList(t.sender(), t.nonce())));
return address;
return _ts.creation ? right160(sha3(rlpList(ts.to, ts.nonce))) : Address();
}
dev::eth::ExecutionResult MixClient::call(Address const& _from, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, bool _gasAuto, FudgeFactor _ff)
@ -335,16 +327,6 @@ dev::eth::ExecutionResult MixClient::call(Address const& _from, u256 _value, Add
return lastExecution().result;
}
void MixClient::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice)
{
submitTransaction(_secret, _value, _dest, _data, _gas, _gasPrice, false);
}
Address MixClient::submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice)
{
return submitTransaction(_secret, _endowment, _init, _gas, _gasPrice, false);
}
dev::eth::ExecutionResult MixClient::call(Address const& _from, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, eth::FudgeFactor _ff)
{
return call(_from, _value, _dest, _data, _gas, _gasPrice, _blockNumber, false, _ff);

6
mix/MixClient.h

@ -54,14 +54,12 @@ public:
ExecutionResult lastExecution() const;
ExecutionResult execution(unsigned _index) const;
void submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) override;
Address submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice) override;
dev::eth::ExecutionResult call(Address const& _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, eth::BlockNumber _blockNumber = eth::PendingBlock, eth::FudgeFactor _ff = eth::FudgeFactor::Strict) override;
dev::eth::ExecutionResult create(Address const& _secret, u256 _value, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * eth::szabo, eth::BlockNumber _blockNumber = eth::PendingBlock, eth::FudgeFactor _ff = eth::FudgeFactor::Strict) override;
using ClientBase::submitTransaction;
void submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, bool _gasAuto);
Address submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice, bool _gasAuto);
virtual Address submitTransaction(eth::TransactionSkeleton const& _ts, Secret const& _secret) override { return submitTransaction(_ts, _secret, false); }
Address submitTransaction(eth::TransactionSkeleton const& _ts, Secret const& _secret, bool _gasAuto);
dev::eth::ExecutionResult call(Address const& _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, eth::BlockNumber _blockNumber, bool _gasAuto, eth::FudgeFactor _ff = eth::FudgeFactor::Strict);
void setAddress(Address _us) override;

4
test/TestHelper.cpp

@ -199,10 +199,10 @@ void ImportTest::importState(json_spirit::mObject& _o, State& _state, stateOptio
stateOptions.m_bHasCode = true;
}
if (code.size())
if (!code.empty())
{
_state.m_cache[address] = Account(balance, Account::ContractConception);
_state.m_cache[address].setCode(code);
_state.m_cache[address].setCode(std::move(code));
}
else
_state.m_cache[address] = Account(balance, Account::NormalCreation);

106
test/libdevcore/FixedHash.cpp

@ -0,0 +1,106 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file FixedHash.cpp
* @author Lefterus <lefteris@ethdev.com>
* @date 2015
*/
#include <libdevcore/FixedHash.h>
#include "../TestHelper.h"
using namespace std;
using namespace dev;
namespace dev
{
namespace test
{
BOOST_AUTO_TEST_SUITE(FixedHashTests)
BOOST_AUTO_TEST_CASE(FixedHashComparisons)
{
FixedHash<4> h1(sha3("abcd"));
FixedHash<4> h2(sha3("abcd"));
FixedHash<4> h3(sha3("aadd"));
FixedHash<4> h4(0xBAADF00D);
FixedHash<4> h5(0xAAAAAAAA);
FixedHash<4> h6(0xBAADF00D);
BOOST_CHECK(h1 == h2);
BOOST_CHECK(h2 != h3);
BOOST_CHECK(h4 > h5);
BOOST_CHECK(h5 < h4);
BOOST_CHECK(h6 <= h4);
BOOST_CHECK(h6 >= h4);
}
BOOST_AUTO_TEST_CASE(FixedHashXOR)
{
FixedHash<2> h1("0xAAAA");
FixedHash<2> h2("0xBBBB");
BOOST_CHECK((h1 ^ h2) == FixedHash<2>("0x1111"));
h1 ^= h2;
BOOST_CHECK(h1 == FixedHash<2>("0x1111"));
}
BOOST_AUTO_TEST_CASE(FixedHashOR)
{
FixedHash<4> h1("0xD3ADB33F");
FixedHash<4> h2("0xBAADF00D");
FixedHash<4> res("0xFBADF33F");
BOOST_CHECK((h1 | h2) == res);
h1 |= h2;
BOOST_CHECK(h1 == res);
}
BOOST_AUTO_TEST_CASE(FixedHashAND)
{
FixedHash<4> h1("0xD3ADB33F");
FixedHash<4> h2("0xBAADF00D");
FixedHash<4> h3("0x92aDB00D");
BOOST_CHECK((h1 & h2) == h3);
h1 &= h2;
BOOST_CHECK(h1 = h3);
}
BOOST_AUTO_TEST_CASE(FixedHashInvert)
{
FixedHash<4> h1("0xD3ADB33F");
FixedHash<4> h2("0x2C524CC0");
BOOST_CHECK(~h1 == h2);
}
BOOST_AUTO_TEST_CASE(FixedHashContains)
{
FixedHash<4> h1("0xD3ADB331");
FixedHash<4> h2("0x0000B331");
FixedHash<4> h3("0x0000000C");
BOOST_CHECK(h1.contains(h2));
BOOST_CHECK(!h1.contains(h3));
}
BOOST_AUTO_TEST_SUITE_END()
}
}

854
test/libethereum/StateTestsFiller/stPreCompiledContractsTransactionFiller.json

File diff suppressed because one or more lines are too long

1
test/libethereum/blockchain.cpp

@ -24,6 +24,7 @@
#include <libdevcore/FileSystem.h>
#include <libdevcore/TransientDirectory.h>
#include <libethereum/CanonBlockChain.h>
#include <libethereum/TransactionQueue.h>
#include <test/TestHelper.h>
using namespace std;

5
test/libethereum/state.cpp

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

248
test/libsolidity/SolidityWallet.cpp

@ -47,12 +47,18 @@ static char const* walletCode = R"DELIMITER(
// some number (specified in constructor) of the set of owners (specified in the constructor, modifiable) before the
// interior is executed.
contract multiowned {
// TYPES
// struct for the status of a pending operation.
struct PendingState {
uint yetNeeded;
uint ownersDone;
uint index;
}
// EVENTS
// 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);
@ -63,14 +69,9 @@ contract multiowned {
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;
}
// MODIFIERS
// simple single-sig function modifier.
modifier onlyowner {
if (isOwner(msg.sender))
@ -80,9 +81,21 @@ contract multiowned {
// that later attempts can be realised as the same underlying operation and
// thus count as confirmations.
modifier onlymanyowners(bytes32 _operation) {
if (confirmed(_operation))
if (confirmAndCheck(_operation))
_
}
// METHODS
// 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;
}
// Revokes a prior confirmation of the given operation
function revoke(bytes32 _operation) external {
uint ownerIndex = m_ownerIndex[uint(msg.sender)];
@ -96,7 +109,75 @@ contract multiowned {
Revoke(msg.sender, _operation);
}
}
function confirmed(bytes32 _operation) internal returns (bool) {
// Replaces an owner `_from` with another `_to`.
function changeOwner(address _from, address _to) onlymanyowners(sha3(msg.data, block.number)) 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, block.number)) 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, block.number)) 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 changeRequirement(uint _newRequired) onlymanyowners(sha3(msg.data, block.number)) external {
if (_newRequired > m_numOwners) return;
m_required = _newRequired;
clearPending();
RequirementChanged(_newRequired);
}
function isOwner(address _addr) returns (bool) {
return m_ownerIndex[uint(_addr)] > 0;
}
function hasConfirmed(bytes32 _operation, address _owner) constant returns (bool) {
var pending = m_pending[_operation];
uint ownerIndex = m_ownerIndex[uint(_owner)];
// make sure they're an owner
if (ownerIndex == 0) return false;
// determine the bit to set for this owner.
uint ownerIndexBit = 2**ownerIndex;
if (pending.ownersDone & ownerIndexBit == 0) {
return false;
} else {
return true;
}
}
// INTERNAL METHODS
function confirmAndCheck(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
@ -132,42 +213,7 @@ contract multiowned {
}
}
}
// 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)
@ -182,6 +228,7 @@ contract multiowned {
}
}
}
function clearPending() internal {
uint length = m_pendingIndex.length;
for (uint i = 0; i < length; ++i)
@ -189,46 +236,54 @@ contract multiowned {
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;
}
// FIELDS
// the number of owners that must confirm the same operation before it is run.
uint public m_required;
// pointer used to find a free slot in m_owners
uint public m_numOwners;
// list of owners
uint[256] public m_owners;
uint[256] m_owners;
uint constant c_maxOwners = 250;
// index on the list of owners to allow reverse lookup
mapping(uint => uint) public m_ownerIndex;
mapping(uint => uint) m_ownerIndex;
// the ongoing operations.
mapping(bytes32 => PendingState) public m_pending;
bytes32[] public m_pendingIndex;
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 {
// MODIFIERS
// simple modifier for daily limit.
modifier limitedDaily(uint _value) {
if (underLimit(_value))
_
}
// METHODS
// 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 {
function setDailyLimit(uint _newLimit) onlymanyowners(sha3(msg.data, block.number)) 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 {
function resetSpentToday() onlymanyowners(sha3(msg.data, block.number)) external {
m_spentToday = 0;
}
// INTERNAL METHODS
// 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) {
@ -244,60 +299,76 @@ contract daylimit is multiowned {
}
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 public m_spentToday;
// FIELDS
uint public m_dailyLimit;
uint public m_lastDay;
uint m_spentToday;
uint m_lastDay;
}
// interface contract for multisig proxy contracts; see below for docs.
contract multisig {
// EVENTS
// 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);
// Multi-sig transaction going out of the wallet (record who signed for it last, the operation hash, how much, and to whom it's going).
event MultiTransact(address owner, bytes32 operation, uint value, address to, bytes data);
// Confirmation still needed for a transaction.
event ConfirmationNeeded(bytes32 operation, address initiator, uint value, address to, bytes data);
// FUNCTIONS
// TODO: document
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 {
// TYPES
// 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);
// Multi-sig transaction going out of the wallet (record who signed for it last, the operation hash, how much, and to whom it's going).
event MultiTransact(address owner, bytes32 operation, uint value, address to, bytes data);*/
// EVENTS
event Created(bytes32 indexed identifier);
// METHODS
// constructor - just pass on the owner arra to the multiowned.
event Created();
function Wallet() {
Created();
function Wallet(bytes32 identifier) {
Created(identifier);
}
// kills the contract sending everything to `_to`.
function kill(address _to) onlymanyowners(sha3(msg.data)) external {
function kill(address _to) onlymanyowners(sha3(msg.data, block.number)) 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
@ -311,7 +382,7 @@ contract Wallet is multisig, multiowned, daylimit {
return 0;
}
// determine our operation hash.
_r = sha3(msg.data);
_r = sha3(msg.data, block.number);
if (!confirm(_r) && m_txs[_r].to == 0) {
m_txs[_r].to = _to;
m_txs[_r].value = _value;
@ -319,6 +390,7 @@ contract Wallet is multisig, multiowned, daylimit {
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) {
@ -329,20 +401,20 @@ contract Wallet is multisig, multiowned, daylimit {
return true;
}
}
// INTERNAL METHODS
function clearPending() internal {
uint length = m_pendingIndex.length;
for (uint i = 0; i < length; ++i)
delete m_txs[m_pendingIndex[i]];
super.clearPending();
}
// // internally confirm transaction with all of the info. returns true iff confirmed good and executed.
// function confirmVerbose(bytes32 _h, address _to, uint _value, bytes _data) private onlymanyowners(_h) returns (bool) {
// _to.call.value(_value)(_data);
// MultiTransact("out", msg.sender, _h, _value, _to);
// return true;
// }
// FIELDS
// pending transactions we have at present.
mapping (bytes32 => Transaction) public m_txs;
mapping (bytes32 => Transaction) m_txs;
}
)DELIMITER";
@ -443,7 +515,7 @@ BOOST_AUTO_TEST_CASE(multisig_value_transfer)
// 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");
h256 opHash("8f27f478ebcfaf28b0c354f4809ace8087000d668b89c8bc3b1b608bfdbe6654");
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));

Loading…
Cancel
Save