Browse Source

Merge branch 'develop' into amd_chunk_dag_upload

cl-refactor
Lefteris Karapetsas 10 years ago
parent
commit
862b5af66c
  1. 4
      alethzero/Debugger.cpp
  2. 6
      alethzero/MainWin.cpp
  3. 14
      eth/main.cpp
  4. 12
      ethminer/MinerAux.h
  5. 10
      evmjit/libevmjit-cpp/Env.cpp
  6. 16
      evmjit/libevmjit-cpp/JitVM.cpp
  7. 10
      evmjit/libevmjit-cpp/JitVM.h
  8. 18
      libdevcore/Common.h
  9. 18
      libdevcore/TransientDirectory.cpp
  10. 520
      libdevcore/boost_multiprecision_number_compare_bug_workaround.hpp
  11. 68
      libethash-cl/ethash_cl_miner.cpp
  12. 1
      libethash-cl/ethash_cl_miner.h
  13. 13
      libethash/internal.c
  14. 21
      libethash/io.c
  15. 17
      libethash/io.h
  16. 5
      libethcore/Ethash.cpp
  17. 2
      libethcore/Ethash.h
  18. 10
      libethcore/EthashAux.cpp
  19. 15
      libethcore/Exceptions.h
  20. 14
      libethereum/BlockQueue.cpp
  21. 2
      libethereum/BlockQueue.h
  22. 8
      libethereum/Client.cpp
  23. 138
      libethereum/EthereumHost.cpp
  24. 9
      libethereum/EthereumHost.h
  25. 20
      libethereum/EthereumPeer.cpp
  26. 100
      libethereum/Executive.cpp
  27. 11
      libethereum/Executive.h
  28. 4
      libethereum/ExtVM.cpp
  29. 43
      libethereum/State.cpp
  30. 21
      libethereum/Transaction.cpp
  31. 3
      libethereum/Transaction.h
  32. 2
      libevm/ExtVMFace.h
  33. 10
      libevm/SmartVM.cpp
  34. 8
      libevm/SmartVM.h
  35. 37
      libevm/VM.cpp
  36. 15
      libevm/VM.h
  37. 5
      libevm/VMFace.h
  38. 14
      libevm/VMFactory.cpp
  39. 4
      libevm/VMFactory.h
  40. 42
      libevmasm/Assembly.cpp
  41. 6
      libevmasm/Assembly.h
  42. 2
      libevmasm/AssemblyItem.h
  43. 75
      libevmasm/BlockDeduplicator.cpp
  44. 16
      libevmasm/BlockDeduplicator.h
  45. 8
      libevmasm/ControlFlowGraph.cpp
  46. 6
      libp2p/HostCapability.cpp
  47. 4
      libp2p/HostCapability.h
  48. 10
      libp2p/Session.cpp
  49. 10
      libp2p/UDP.h
  50. 2
      libsolidity/AST.cpp
  51. 22
      libsolidity/ArrayUtils.cpp
  52. 2
      libsolidity/Compiler.cpp
  53. 2
      libsolidity/CompilerContext.h
  54. 10
      libsolidity/ExpressionCompiler.cpp
  55. 5
      libsolidity/ExpressionCompiler.h
  56. 2
      libsolidity/Token.h
  57. 20
      libsolidity/Types.cpp
  58. 22
      libsolidity/Types.h
  59. 12
      libweb3jsonrpc/WebThreeStubServerBase.cpp
  60. 24
      libwhisper/Common.cpp
  61. 28
      libwhisper/Common.h
  62. 24
      libwhisper/Interface.h
  63. 20
      libwhisper/Message.cpp
  64. 20
      libwhisper/Message.h
  65. 4
      libwhisper/WhisperHost.cpp
  66. 6
      libwhisper/WhisperHost.h
  67. 4
      mix/CodeModel.cpp
  68. 18
      mix/MixClient.cpp
  69. 37
      mix/QBigInt.cpp
  70. 3
      mix/QBigInt.h
  71. 1
      mix/SolidityType.h
  72. 1
      mix/qml.qrc
  73. 7
      mix/qml/QIntTypeView.qml
  74. 23
      mix/qml/TransactionDialog.qml
  75. 125
      mix/qml/js/InputValidator.js
  76. 57
      solc/jsonCompiler.cpp
  77. 1
      test/CMakeLists.txt
  78. 7
      test/TestHelper.cpp
  79. 1
      test/TestHelper.h
  80. 5
      test/external-dependencies/CMakeLists.txt
  81. 37
      test/external-dependencies/boost.cpp
  82. 7
      test/fuzzTesting/CMakeLists.txt
  83. 8
      test/fuzzTesting/checkRandomVMTest.cpp
  84. 204
      test/fuzzTesting/createRandomStateTest.cpp
  85. 8
      test/fuzzTesting/createRandomVMTest.cpp
  86. 224
      test/fuzzTesting/fuzzHelper.cpp
  87. 97
      test/fuzzTesting/fuzzHelper.h
  88. 216
      test/libethereum/BlockTestsFiller/bcUncleTestFiller.json
  89. 20
      test/libethereum/TransactionTestsFiller/ttTransactionTestFiller.json
  90. 83
      test/libethereum/blockchain.cpp
  91. 56
      test/libevm/VMTestsFiller/vmPushDupSwapTestFiller.json
  92. 18
      test/libevm/vm.cpp
  93. 27
      test/libsolidity/SolidityABIJSON.cpp
  94. 192
      test/libsolidity/SolidityCompiler.cpp
  95. 66
      test/libsolidity/SolidityEndToEndTest.cpp
  96. 33
      test/libsolidity/SolidityNameAndTypeResolution.cpp
  97. 32
      test/libsolidity/SolidityOptimizer.cpp
  98. 43
      test/libsolidity/solidityExecutionFramework.h
  99. 8
      test/libwhisper/whisperMessage.cpp

4
alethzero/Debugger.cpp

@ -82,7 +82,7 @@ bool DebugSession::populate(dev::eth::Executive& _executive, dev::eth::Transacti
bytesConstRef lastData;
h256 lastHash;
h256 lastDataHash;
auto onOp = [&](uint64_t steps, Instruction inst, dev::bigint newMemSize, dev::bigint gasCost, VM* voidVM, ExtVMFace const* voidExt)
auto onOp = [&](uint64_t steps, Instruction inst, bigint newMemSize, bigint gasCost, bigint gas, VM* voidVM, ExtVMFace const* voidExt)
{
VM& vm = *voidVM;
ExtVM const& ext = *static_cast<ExtVM const*>(voidExt);
@ -104,7 +104,7 @@ bool DebugSession::populate(dev::eth::Executive& _executive, dev::eth::Transacti
levels.push_back(&history.back());
else
levels.resize(ext.depth);
history.append(WorldState({steps, ext.myAddress, vm.curPC(), inst, newMemSize, vm.gas(), lastHash, lastDataHash, vm.stack(), vm.memory(), gasCost, ext.state().storage(ext.myAddress), levels}));
history.append(WorldState({steps, ext.myAddress, vm.curPC(), inst, newMemSize, static_cast<u256>(gas), lastHash, lastDataHash, vm.stack(), vm.memory(), gasCost, ext.state().storage(ext.myAddress), levels}));
};
_executive.go(onOp);
_executive.finalize();

6
alethzero/MainWin.cpp

@ -1803,7 +1803,7 @@ void Main::on_accounts_doubleClicked()
}
}
static shh::FullTopic topicFromText(QString _s)
static shh::Topics topicFromText(QString _s)
{
shh::BuildTopic ret;
while (_s.size())
@ -2187,10 +2187,10 @@ void Main::refreshWhispers()
shh::Envelope const& e = w.second;
shh::Message m;
for (pair<Public, Secret> const& i: m_server->ids())
if (!!(m = e.open(shh::FullTopic(), i.second)))
if (!!(m = e.open(shh::Topics(), i.second)))
break;
if (!m)
m = e.open(shh::FullTopic());
m = e.open(shh::Topics());
QString msg;
if (m.from())

14
eth/main.cpp

@ -628,7 +628,7 @@ int main(int argc, char** argv)
nodeMode == NodeMode::Full ? set<string>{"eth"/*, "shh"*/} : set<string>(),
netPrefs,
&nodesState);
auto toNumber = [&](string const& s) -> unsigned {
if (s == "latest")
return web3.ethereum()->number();
@ -1187,7 +1187,7 @@ int main(int argc, char** argv)
{
OnOpFunc oof;
if (format == "pretty")
oof = [&](uint64_t steps, Instruction instr, bigint newMemSize, bigint gasCost, dev::eth::VM* vvm, dev::eth::ExtVMFace const* vextVM)
oof = [&](uint64_t steps, Instruction instr, bigint newMemSize, bigint gasCost, bigint gas, dev::eth::VM* vvm, dev::eth::ExtVMFace const* vextVM)
{
dev::eth::VM* vm = vvm;
dev::eth::ExtVM const* ext = static_cast<ExtVM const*>(vextVM);
@ -1198,24 +1198,24 @@ int main(int argc, char** argv)
f << " STORAGE" << endl;
for (auto const& i: ext->state().storage(ext->myAddress))
f << showbase << hex << i.first << ": " << i.second << endl;
f << dec << ext->depth << " | " << ext->myAddress << " | #" << steps << " | " << hex << setw(4) << setfill('0') << vm->curPC() << " : " << dev::eth::instructionInfo(instr).name << " | " << dec << vm->gas() << " | -" << dec << gasCost << " | " << newMemSize << "x32";
f << dec << ext->depth << " | " << ext->myAddress << " | #" << steps << " | " << hex << setw(4) << setfill('0') << vm->curPC() << " : " << dev::eth::instructionInfo(instr).name << " | " << dec << gas << " | -" << dec << gasCost << " | " << newMemSize << "x32";
};
else if (format == "standard")
oof = [&](uint64_t, Instruction instr, bigint, bigint, dev::eth::VM* vvm, dev::eth::ExtVMFace const* vextVM)
oof = [&](uint64_t, Instruction instr, bigint, bigint, bigint gas, dev::eth::VM* vvm, dev::eth::ExtVMFace const* vextVM)
{
dev::eth::VM* vm = vvm;
dev::eth::ExtVM const* ext = static_cast<ExtVM const*>(vextVM);
f << ext->myAddress << " " << hex << toHex(dev::toCompactBigEndian(vm->curPC(), 1)) << " " << hex << toHex(dev::toCompactBigEndian((int)(byte)instr, 1)) << " " << hex << toHex(dev::toCompactBigEndian((uint64_t)vm->gas(), 1)) << endl;
f << ext->myAddress << " " << hex << toHex(dev::toCompactBigEndian(vm->curPC(), 1)) << " " << hex << toHex(dev::toCompactBigEndian((int)(byte)instr, 1)) << " " << hex << toHex(dev::toCompactBigEndian((uint64_t)gas, 1)) << endl;
};
else if (format == "standard+")
oof = [&](uint64_t, Instruction instr, bigint, bigint, dev::eth::VM* vvm, dev::eth::ExtVMFace const* vextVM)
oof = [&](uint64_t, Instruction instr, bigint, bigint, bigint gas, dev::eth::VM* vvm, dev::eth::ExtVMFace const* vextVM)
{
dev::eth::VM* vm = vvm;
dev::eth::ExtVM const* ext = static_cast<ExtVM const*>(vextVM);
if (instr == Instruction::STOP || instr == Instruction::RETURN || instr == Instruction::SUICIDE)
for (auto const& i: ext->state().storage(ext->myAddress))
f << toHex(dev::toCompactBigEndian(i.first, 1)) << " " << toHex(dev::toCompactBigEndian(i.second, 1)) << endl;
f << ext->myAddress << " " << hex << toHex(dev::toCompactBigEndian(vm->curPC(), 1)) << " " << hex << toHex(dev::toCompactBigEndian((int)(byte)instr, 1)) << " " << hex << toHex(dev::toCompactBigEndian((uint64_t)vm->gas(), 1)) << endl;
f << ext->myAddress << " " << hex << toHex(dev::toCompactBigEndian(vm->curPC(), 1)) << " " << hex << toHex(dev::toCompactBigEndian((int)(byte)instr, 1)) << " " << hex << toHex(dev::toCompactBigEndian((uint64_t)gas, 1)) << endl;
};
e.initialize(t);
if (!e.execute())

12
ethminer/MinerAux.h

@ -175,8 +175,16 @@ public:
m_minerType = MinerType::CPU;
else if (arg == "-G" || arg == "--opencl")
{
m_minerType = MinerType::GPU;
miningThreads = 1;
if (!ProofOfWork::GPUMiner::haveSufficientGPUMemory())
{
cout << "No GPU device with sufficient memory was found. Defaulting to CPU" << endl;
m_minerType = MinerType::CPU;
}
else
{
m_minerType = MinerType::GPU;
miningThreads = 1;
}
}
else if (arg == "--no-precompute")
{

10
evmjit/libevmjit-cpp/Env.cpp

@ -54,7 +54,7 @@ extern "C"
if (_env->balance(_env->myAddress) >= endowment && _env->depth < 1024)
{
u256 gas = *io_gas;
h256 address(_env->create(endowment, gas, {_initBeg, _initSize}, {}), h256::AlignRight);
h256 address(_env->create(endowment, gas, {_initBeg, (size_t)_initSize}, {}), h256::AlignRight);
*io_gas = static_cast<int64_t>(gas);
*o_address = address;
}
@ -69,8 +69,8 @@ extern "C"
params.senderAddress = _env->myAddress;
params.receiveAddress = right160(*_receiveAddress);
params.codeAddress = right160(*_codeAddress);
params.data = {_inBeg, _inSize};
params.out = {_outBeg, _outSize};
params.data = {_inBeg, (size_t)_inSize};
params.out = {_outBeg, (size_t)_outSize};
params.onOp = {};
const auto isCall = params.receiveAddress == params.codeAddress; // OPT: The same address pointer can be used if not CODECALL
@ -102,7 +102,7 @@ extern "C"
EXPORT void env_sha3(byte* _begin, uint64_t _size, h256* o_hash)
{
auto hash = sha3({_begin, _size});
auto hash = sha3({_begin, (size_t)_size});
*o_hash = hash;
}
@ -130,7 +130,7 @@ extern "C"
if (_topic4)
topics.push_back(*_topic4);
_env->log(std::move(topics), {_beg, _size});
_env->log(std::move(topics), {_beg, (size_t)_size});
}
}

16
evmjit/libevmjit-cpp/JitVM.cpp

@ -18,27 +18,25 @@ namespace eth
extern "C" void env_sload(); // fake declaration for linker symbol stripping workaround, see a call below
bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step)
bytesConstRef JitVM::go(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step)
{
using namespace jit;
auto rejected = false;
// TODO: Rejecting transactions with gas limit > 2^63 can be used by attacker to take JIT out of scope
rejected |= m_gas > std::numeric_limits<decltype(m_data.gas)>::max(); // Do not accept requests with gas > 2^63 (int64 max)
rejected |= io_gas > std::numeric_limits<decltype(m_data.gas)>::max(); // Do not accept requests with gas > 2^63 (int64 max)
rejected |= _ext.gasPrice > std::numeric_limits<decltype(m_data.gasPrice)>::max();
rejected |= _ext.currentBlock.number > std::numeric_limits<decltype(m_data.number)>::max();
rejected |= _ext.currentBlock.timestamp > std::numeric_limits<decltype(m_data.timestamp)>::max();
if (rejected)
{
cwarn << "Execution rejected by EVM JIT (gas limit: " << m_gas << "), executing with interpreter";
m_fallbackVM = VMFactory::create(VMKind::Interpreter, m_gas);
auto&& output = m_fallbackVM->go(_ext, _onOp, _step);
m_gas = m_fallbackVM->gas(); // copy remaining gas, Executive expects it
return output;
cwarn << "Execution rejected by EVM JIT (gas limit: " << io_gas << "), executing with interpreter";
m_fallbackVM = VMFactory::create(VMKind::Interpreter);
return m_fallbackVM->go(io_gas, _ext, _onOp, _step);
}
m_data.gas = static_cast<decltype(m_data.gas)>(m_gas);
m_data.gas = static_cast<decltype(m_data.gas)>(io_gas);
m_data.gasPrice = static_cast<decltype(m_data.gasPrice)>(_ext.gasPrice);
m_data.callData = _ext.data.data();
m_data.callDataSize = _ext.data.size();
@ -78,7 +76,7 @@ bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step)
break;
}
m_gas = m_data.gas; // TODO: Remove m_gas field
io_gas = m_data.gas;
return {std::get<0>(m_engine.returnData), std::get<1>(m_engine.returnData)};
}

10
evmjit/libevmjit-cpp/JitVM.h

@ -10,16 +10,10 @@ namespace eth
class JitVM: public VMFace
{
virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final;
virtual u256 gas() const noexcept { return m_gas; }
virtual void reset(u256 const& _gas = 0) noexcept { m_gas = _gas; }
public:
virtual bytesConstRef go(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final;
private:
friend class VMFactory;
explicit JitVM(u256 _gas = 0): m_gas(_gas) {}
u256 m_gas;
jit::RuntimeData m_data;
jit::ExecutionEngine m_engine;
std::unique_ptr<VMFace> m_fallbackVM; ///< VM used in case of input data rejected by JIT

18
libdevcore/Common.h

@ -45,6 +45,10 @@
#pragma warning(push)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#include <boost/version.hpp>
#if (BOOST_VERSION == 105800)
#include "boost_multiprecision_number_compare_bug_workaround.hpp"
#endif
#include <boost/multiprecision/cpp_int.hpp>
#pragma warning(pop)
#pragma GCC diagnostic pop
@ -85,20 +89,6 @@ using u160s = std::vector<u160>;
using u256Set = std::set<u256>;
using u160Set = std::set<u160>;
// Workaround for mixed type big int comparison bug introduced in boost 1.58
// https://svn.boost.org/trac/boost/ticket/11328
#define ETH_BIGINT_WRONG_COMPARISON_WORKAROUND(_OP) \
template<typename _T, typename _U> auto operator _OP (_T const& a, _U const& b) -> typename std::enable_if<std::is_same<_T, u256>::value && std::is_same<_U, bigint>::value, bool>::type { return (bigint)a _OP b; } \
template<typename _T, typename _U> auto operator _OP (_U const& a, _T const& b) -> typename std::enable_if<std::is_same<_T, u256>::value && std::is_same<_U, bigint>::value, bool>::type { return a _OP (bigint)b; }
ETH_BIGINT_WRONG_COMPARISON_WORKAROUND(==)
ETH_BIGINT_WRONG_COMPARISON_WORKAROUND(!=)
ETH_BIGINT_WRONG_COMPARISON_WORKAROUND(>=)
ETH_BIGINT_WRONG_COMPARISON_WORKAROUND(<=)
ETH_BIGINT_WRONG_COMPARISON_WORKAROUND(<)
ETH_BIGINT_WRONG_COMPARISON_WORKAROUND(>)
extern const u256 UndefinedU256;
// Map types.

18
libdevcore/TransientDirectory.cpp

@ -19,10 +19,12 @@
* @date 2015
*/
#include <thread>
#include <boost/filesystem.hpp>
#include "Exceptions.h"
#include "TransientDirectory.h"
#include "CommonIO.h"
#include "Log.h"
using namespace std;
using namespace dev;
@ -42,5 +44,19 @@ TransientDirectory::TransientDirectory(std::string const& _path):
TransientDirectory::~TransientDirectory()
{
boost::filesystem::remove_all(m_path);
boost::system::error_code ec;
boost::filesystem::remove_all(m_path, ec);
if (!ec)
return;
// In some cases, antivirus runnig on Windows will scan all the newly created directories.
// As a consequence, directory is locked and can not be deleted immediately.
// Retry after 10 milliseconds usually is successful.
// This will help our tests run smoothly in such environment.
this_thread::sleep_for(chrono::milliseconds(10));
ec.clear();
boost::filesystem::remove_all(m_path, ec);
if (!ec)
cwarn << "Failed to delete directory '" << m_path << "': " << ec.message();
}

520
libdevcore/boost_multiprecision_number_compare_bug_workaround.hpp

@ -0,0 +1,520 @@
// This is a copy of boost/multiprecision/detail/number_compare.hpp from boost 1.59 to replace buggy version from 1.58.
#ifdef BOOST_MP_COMPARE_HPP
#error This bug workaround header must be included before original boost/multiprecision/detail/number_compare.hpp
#endif
///////////////////////////////////////////////////////////////////////////////
// Copyright 2012 John Maddock. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_MP_COMPARE_HPP
#define BOOST_MP_COMPARE_HPP
// A copy of boost/multiprecision/traits/is_backend.hpp
#ifndef BOOST_MP_IS_BACKEND_HPP
#define BOOST_MP_IS_BACKEND_HPP
#include <boost/mpl/has_xxx.hpp>
#include <boost/type_traits/conditional.hpp>
#include <boost/type_traits/is_convertible.hpp>
#include <boost/multiprecision/detail/number_base.hpp>
#include <boost/multiprecision/detail/generic_interconvert.hpp>
namespace boost{ namespace multiprecision{ namespace detail{
BOOST_MPL_HAS_XXX_TRAIT_DEF(signed_types)
BOOST_MPL_HAS_XXX_TRAIT_DEF(unsigned_types)
BOOST_MPL_HAS_XXX_TRAIT_DEF(float_types)
template <class T>
struct is_backend
{
static const bool value = has_signed_types<T>::value && has_unsigned_types<T>::value && has_float_types<T>::value;
};
template <class Backend>
struct other_backend
{
typedef typename boost::conditional<
boost::is_same<number<Backend>, number<Backend, et_on> >::value,
number<Backend, et_off>, number<Backend, et_on> >::type type;
};
template <class B, class V>
struct number_from_backend
{
typedef typename boost::conditional <
boost::is_convertible<V, number<B> >::value,
number<B>,
typename other_backend<B>::type > ::type type;
};
template <bool b, class T, class U>
struct is_first_backend_imp{ static const bool value = false; };
template <class T, class U>
struct is_first_backend_imp<true, T, U>{ static const bool value = is_convertible<U, number<T, et_on> >::value || is_convertible<U, number<T, et_off> >::value; };
template <class T, class U>
struct is_first_backend : is_first_backend_imp<is_backend<T>::value, T, U> {};
template <bool b, class T, class U>
struct is_second_backend_imp{ static const bool value = false; };
template <class T, class U>
struct is_second_backend_imp<true, T, U>{ static const bool value = is_convertible<T, number<U> >::value || is_convertible<T, number<U, et_off> >::value; };
template <class T, class U>
struct is_second_backend : is_second_backend_imp<is_backend<U>::value, T, U> {};
}
}
}
#endif // BOOST_MP_IS_BACKEND_HPP
//
// Comparison operators for number.
//
namespace boost{ namespace multiprecision{
namespace default_ops{
template <class B>
inline bool eval_eq(const B& a, const B& b)
{
return a.compare(b) == 0;
}
template <class T, class U>
inline typename enable_if_c<boost::multiprecision::detail::is_first_backend<T, U>::value, bool>::type eval_eq(const T& a, const U& b)
{
typename boost::multiprecision::detail::number_from_backend<T, U>::type t(b);
return eval_eq(a, t.backend());
}
template <class T, class U>
inline typename enable_if_c<boost::multiprecision::detail::is_second_backend<T, U>::value, bool>::type eval_eq(const T& a, const U& b)
{
typename boost::multiprecision::detail::number_from_backend<U, T>::type t(a);
return eval_eq(t.backend(), b);
}
template <class B>
inline bool eval_lt(const B& a, const B& b)
{
return a.compare(b) < 0;
}
template <class T, class U>
inline typename enable_if_c<boost::multiprecision::detail::is_first_backend<T, U>::value, bool>::type eval_lt(const T& a, const U& b)
{
typename boost::multiprecision::detail::number_from_backend<T, U>::type t(b);
return eval_lt(a, t.backend());
}
template <class T, class U>
inline typename enable_if_c<boost::multiprecision::detail::is_second_backend<T, U>::value, bool>::type eval_lt(const T& a, const U& b)
{
typename boost::multiprecision::detail::number_from_backend<U, T>::type t(a);
return eval_lt(t.backend(), b);
}
template <class B>
inline bool eval_gt(const B& a, const B& b)
{
return a.compare(b) > 0;
}
template <class T, class U>
inline typename enable_if_c<boost::multiprecision::detail::is_first_backend<T, U>::value, bool>::type eval_gt(const T& a, const U& b)
{
typename boost::multiprecision::detail::number_from_backend<T, U>::type t(b);
return eval_gt(a, t.backend());
}
template <class T, class U>
inline typename enable_if_c<boost::multiprecision::detail::is_second_backend<T, U>::value, bool>::type eval_gt(const T& a, const U& b)
{
typename boost::multiprecision::detail::number_from_backend<U, T>::type t(a);
return eval_gt(t.backend(), b);
}
} // namespace default_ops
namespace detail{
template <class Num, class Val>
struct is_valid_mixed_compare : public mpl::false_ {};
template <class B, expression_template_option ET, class Val>
struct is_valid_mixed_compare<number<B, ET>, Val> : public is_convertible<Val, number<B, ET> > {};
template <class B, expression_template_option ET>
struct is_valid_mixed_compare<number<B, ET>, number<B, ET> > : public mpl::false_ {};
template <class B, expression_template_option ET, class tag, class Arg1, class Arg2, class Arg3, class Arg4>
struct is_valid_mixed_compare<number<B, ET>, expression<tag, Arg1, Arg2, Arg3, Arg4> >
: public mpl::bool_<is_convertible<expression<tag, Arg1, Arg2, Arg3, Arg4>, number<B, ET> >::value> {};
template <class tag, class Arg1, class Arg2, class Arg3, class Arg4, class B, expression_template_option ET>
struct is_valid_mixed_compare<expression<tag, Arg1, Arg2, Arg3, Arg4>, number<B, ET> >
: public mpl::bool_<is_convertible<expression<tag, Arg1, Arg2, Arg3, Arg4>, number<B, ET> >::value> {};
template <class Backend, expression_template_option ExpressionTemplates>
inline BOOST_CONSTEXPR typename boost::enable_if_c<number_category<Backend>::value != number_kind_floating_point, bool>::type is_unordered_value(const number<Backend, ExpressionTemplates>&)
{
return false;
}
template <class Backend, expression_template_option ExpressionTemplates>
inline BOOST_CONSTEXPR typename boost::enable_if_c<number_category<Backend>::value == number_kind_floating_point, bool>::type is_unordered_value(const number<Backend, ExpressionTemplates>& a)
{
using default_ops::eval_fpclassify;
return eval_fpclassify(a.backend()) == FP_NAN;
}
template <class Arithmetic>
inline BOOST_CONSTEXPR typename boost::enable_if_c<number_category<Arithmetic>::value != number_kind_floating_point, bool>::type is_unordered_value(const Arithmetic&)
{
return false;
}
template <class Arithmetic>
inline BOOST_CONSTEXPR typename boost::enable_if_c<number_category<Arithmetic>::value == number_kind_floating_point, bool>::type is_unordered_value(const Arithmetic& a)
{
return (boost::math::isnan)(a);
}
template <class T, class U>
inline BOOST_CONSTEXPR bool is_unordered_comparison(const T& a, const U& b)
{
return is_unordered_value(a) || is_unordered_value(b);
}
}
template <class Backend, expression_template_option ExpressionTemplates, class Backend2, expression_template_option ExpressionTemplates2>
inline bool operator == (const number<Backend, ExpressionTemplates>& a, const number<Backend2, ExpressionTemplates2>& b)
{
using default_ops::eval_eq;
if(detail::is_unordered_comparison(a, b)) return false;
return eval_eq(a.backend(), b.backend());
}
template <class Backend, expression_template_option ExpressionTemplates, class Arithmetic>
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
operator == (const number<Backend, ExpressionTemplates>& a, const Arithmetic& b)
{
using default_ops::eval_eq;
if(detail::is_unordered_comparison(a, b)) return false;
return eval_eq(a.backend(), number<Backend, ExpressionTemplates>::canonical_value(b));
}
template <class Arithmetic, class Backend, expression_template_option ExpressionTemplates>
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
operator == (const Arithmetic& a, const number<Backend, ExpressionTemplates>& b)
{
using default_ops::eval_eq;
if(detail::is_unordered_comparison(a, b)) return false;
return eval_eq(b.backend(), number<Backend, ExpressionTemplates>::canonical_value(a));
}
template <class Arithmetic, class Tag, class A1, class A2, class A3, class A4>
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
operator == (const Arithmetic& a, const detail::expression<Tag, A1, A2, A3, A4>& b)
{
typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type;
using default_ops::eval_eq;
result_type t(b);
if(detail::is_unordered_comparison(a, t)) return false;
return eval_eq(t.backend(), result_type::canonical_value(a));
}
template <class Tag, class A1, class A2, class A3, class A4, class Arithmetic>
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
operator == (const detail::expression<Tag, A1, A2, A3, A4>& a, const Arithmetic& b)
{
typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type;
using default_ops::eval_eq;
result_type t(a);
if(detail::is_unordered_comparison(t, b)) return false;
return eval_eq(t.backend(), result_type::canonical_value(b));
}
template <class Tag, class A1, class A2, class A3, class A4, class Tagb, class A1b, class A2b, class A3b, class A4b>
inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type>, bool>::type
operator == (const detail::expression<Tag, A1, A2, A3, A4>& a, const detail::expression<Tagb, A1b, A2b, A3b, A4b>& b)
{
using default_ops::eval_eq;
typename detail::expression<Tag, A1, A2, A3, A4>::result_type t(a);
typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type t2(b);
if(detail::is_unordered_comparison(t, t2)) return false;
return eval_eq(t.backend(), t2.backend());
}
template <class Backend, expression_template_option ExpressionTemplates, class Backend2, expression_template_option ExpressionTemplates2>
inline bool operator != (const number<Backend, ExpressionTemplates>& a, const number<Backend2, ExpressionTemplates2>& b)
{
using default_ops::eval_eq;
if(detail::is_unordered_comparison(a, b)) return true;
return !eval_eq(a.backend(), b.backend());
}
template <class Backend, expression_template_option ExpressionTemplates, class Arithmetic>
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
operator != (const number<Backend, ExpressionTemplates>& a, const Arithmetic& b)
{
using default_ops::eval_eq;
if(detail::is_unordered_comparison(a, b)) return true;
return !eval_eq(a.backend(), number<Backend, et_on>::canonical_value(b));
}
template <class Arithmetic, class Backend, expression_template_option ExpressionTemplates>
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
operator != (const Arithmetic& a, const number<Backend, ExpressionTemplates>& b)
{
using default_ops::eval_eq;
if(detail::is_unordered_comparison(a, b)) return true;
return !eval_eq(b.backend(), number<Backend, et_on>::canonical_value(a));
}
template <class Arithmetic, class Tag, class A1, class A2, class A3, class A4>
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
operator != (const Arithmetic& a, const detail::expression<Tag, A1, A2, A3, A4>& b)
{
typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type;
using default_ops::eval_eq;
result_type t(b);
if(detail::is_unordered_comparison(a, t)) return true;
return !eval_eq(t.backend(), result_type::canonical_value(a));
}
template <class Tag, class A1, class A2, class A3, class A4, class Arithmetic>
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
operator != (const detail::expression<Tag, A1, A2, A3, A4>& a, const Arithmetic& b)
{
typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type;
using default_ops::eval_eq;
result_type t(a);
if(detail::is_unordered_comparison(t, b)) return true;
return !eval_eq(t.backend(), result_type::canonical_value(b));
}
template <class Tag, class A1, class A2, class A3, class A4, class Tagb, class A1b, class A2b, class A3b, class A4b>
inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type>, bool>::type
operator != (const detail::expression<Tag, A1, A2, A3, A4>& a, const detail::expression<Tagb, A1b, A2b, A3b, A4b>& b)
{
using default_ops::eval_eq;
typename detail::expression<Tag, A1, A2, A3, A4>::result_type t(a);
typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type t2(b);
if(detail::is_unordered_comparison(t, t2)) return true;
return !eval_eq(t.backend(), t2.backend());
}
template <class Backend, expression_template_option ExpressionTemplates, class Backend2, expression_template_option ExpressionTemplates2>
inline bool operator < (const number<Backend, ExpressionTemplates>& a, const number<Backend2, ExpressionTemplates2>& b)
{
using default_ops::eval_lt;
if(detail::is_unordered_comparison(a, b)) return false;
return eval_lt(a.backend(), b.backend());
}
template <class Backend, expression_template_option ExpressionTemplates, class Arithmetic>
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
operator < (const number<Backend, ExpressionTemplates>& a, const Arithmetic& b)
{
using default_ops::eval_lt;
if(detail::is_unordered_comparison(a, b)) return false;
return eval_lt(a.backend(), number<Backend, ExpressionTemplates>::canonical_value(b));
}
template <class Arithmetic, class Backend, expression_template_option ExpressionTemplates>
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
operator < (const Arithmetic& a, const number<Backend, ExpressionTemplates>& b)
{
using default_ops::eval_gt;
if(detail::is_unordered_comparison(a, b)) return false;
return eval_gt(b.backend(), number<Backend, ExpressionTemplates>::canonical_value(a));
}
template <class Arithmetic, class Tag, class A1, class A2, class A3, class A4>
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
operator < (const Arithmetic& a, const detail::expression<Tag, A1, A2, A3, A4>& b)
{
typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type;
using default_ops::eval_gt;
result_type t(b);
if(detail::is_unordered_comparison(a, t)) return false;
return eval_gt(t.backend(), result_type::canonical_value(a));
}
template <class Tag, class A1, class A2, class A3, class A4, class Arithmetic>
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
operator < (const detail::expression<Tag, A1, A2, A3, A4>& a, const Arithmetic& b)
{
typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type;
using default_ops::eval_lt;
result_type t(a);
if(detail::is_unordered_comparison(t, b)) return false;
return eval_lt(t.backend(), result_type::canonical_value(b));
}
template <class Tag, class A1, class A2, class A3, class A4, class Tagb, class A1b, class A2b, class A3b, class A4b>
inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type>, bool>::type
operator < (const detail::expression<Tag, A1, A2, A3, A4>& a, const detail::expression<Tagb, A1b, A2b, A3b, A4b>& b)
{
using default_ops::eval_lt;
typename detail::expression<Tag, A1, A2, A3, A4>::result_type t(a);
typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type t2(b);
if(detail::is_unordered_comparison(t, t2)) return false;
return eval_lt(t.backend(), t2.backend());
}
template <class Backend, expression_template_option ExpressionTemplates, class Backend2, expression_template_option ExpressionTemplates2>
inline bool operator > (const number<Backend, ExpressionTemplates>& a, const number<Backend2, ExpressionTemplates2>& b)
{
using default_ops::eval_gt;
if(detail::is_unordered_comparison(a, b)) return false;
return eval_gt(a.backend(), b.backend());
}
template <class Backend, expression_template_option ExpressionTemplates, class Arithmetic>
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
operator > (const number<Backend, ExpressionTemplates>& a, const Arithmetic& b)
{
using default_ops::eval_gt;
if(detail::is_unordered_comparison(a, b)) return false;
return eval_gt(a.backend(), number<Backend, ExpressionTemplates>::canonical_value(b));
}
template <class Arithmetic, class Backend, expression_template_option ExpressionTemplates>
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
operator > (const Arithmetic& a, const number<Backend, ExpressionTemplates>& b)
{
using default_ops::eval_lt;
if(detail::is_unordered_comparison(a, b)) return false;
return eval_lt(b.backend(), number<Backend, ExpressionTemplates>::canonical_value(a));
}
template <class Arithmetic, class Tag, class A1, class A2, class A3, class A4>
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
operator > (const Arithmetic& a, const detail::expression<Tag, A1, A2, A3, A4>& b)
{
typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type;
using default_ops::eval_lt;
result_type t(b);
if(detail::is_unordered_comparison(a, t)) return false;
return a > t;
}
template <class Tag, class A1, class A2, class A3, class A4, class Arithmetic>
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
operator > (const detail::expression<Tag, A1, A2, A3, A4>& a, const Arithmetic& b)
{
typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type;
using default_ops::eval_gt;
result_type t(a);
if(detail::is_unordered_comparison(t, b)) return false;
return t > b;
}
template <class Tag, class A1, class A2, class A3, class A4, class Tagb, class A1b, class A2b, class A3b, class A4b>
inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type>, bool>::type
operator > (const detail::expression<Tag, A1, A2, A3, A4>& a, const detail::expression<Tagb, A1b, A2b, A3b, A4b>& b)
{
using default_ops::eval_gt;
typename detail::expression<Tag, A1, A2, A3, A4>::result_type t(a);
typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type t2(b);
if(detail::is_unordered_comparison(t, t2)) return false;
return t > t2;
}
template <class Backend, expression_template_option ExpressionTemplates, class Backend2, expression_template_option ExpressionTemplates2>
inline bool operator <= (const number<Backend, ExpressionTemplates>& a, const number<Backend2, ExpressionTemplates2>& b)
{
using default_ops::eval_gt;
if(detail::is_unordered_comparison(a, b)) return false;
return !eval_gt(a.backend(), b.backend());
}
template <class Backend, expression_template_option ExpressionTemplates, class Arithmetic>
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
operator <= (const number<Backend, ExpressionTemplates>& a, const Arithmetic& b)
{
using default_ops::eval_gt;
if(detail::is_unordered_comparison(a, b)) return false;
return !eval_gt(a.backend(), number<Backend, ExpressionTemplates>::canonical_value(b));
}
template <class Arithmetic, class Backend, expression_template_option ExpressionTemplates>
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
operator <= (const Arithmetic& a, const number<Backend, ExpressionTemplates>& b)
{
using default_ops::eval_lt;
if(detail::is_unordered_comparison(a, b)) return false;
return !eval_lt(b.backend(), number<Backend, ExpressionTemplates>::canonical_value(a));
}
template <class Arithmetic, class Tag, class A1, class A2, class A3, class A4>
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
operator <= (const Arithmetic& a, const detail::expression<Tag, A1, A2, A3, A4>& b)
{
typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type;
using default_ops::eval_lt;
if(detail::is_unordered_value(a) || detail::is_unordered_value(b))
return false;
result_type t(b);
if(detail::is_unordered_comparison(a, t)) return false;
return !eval_lt(t.backend(), result_type::canonical_value(a));
}
template <class Tag, class A1, class A2, class A3, class A4, class Arithmetic>
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
operator <= (const detail::expression<Tag, A1, A2, A3, A4>& a, const Arithmetic& b)
{
typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type;
using default_ops::eval_gt;
result_type t(a);
if(detail::is_unordered_comparison(t, b)) return false;
return !eval_gt(t.backend(), result_type::canonical_value(b));
}
template <class Tag, class A1, class A2, class A3, class A4, class Tagb, class A1b, class A2b, class A3b, class A4b>
inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type>, bool>::type
operator <= (const detail::expression<Tag, A1, A2, A3, A4>& a, const detail::expression<Tagb, A1b, A2b, A3b, A4b>& b)
{
using default_ops::eval_gt;
typename detail::expression<Tag, A1, A2, A3, A4>::result_type t(a);
typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type t2(b);
if(detail::is_unordered_comparison(t, t2)) return false;
return !eval_gt(t.backend(), t2.backend());
}
template <class Backend, expression_template_option ExpressionTemplates, class Backend2, expression_template_option ExpressionTemplates2>
inline bool operator >= (const number<Backend, ExpressionTemplates>& a, const number<Backend2, ExpressionTemplates2>& b)
{
using default_ops::eval_lt;
if(detail::is_unordered_comparison(a, b)) return false;
return !eval_lt(a.backend(), b.backend());
}
template <class Backend, expression_template_option ExpressionTemplates, class Arithmetic>
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
operator >= (const number<Backend, ExpressionTemplates>& a, const Arithmetic& b)
{
using default_ops::eval_lt;
if(detail::is_unordered_comparison(a, b)) return false;
return !eval_lt(a.backend(), number<Backend, ExpressionTemplates>::canonical_value(b));
}
template <class Arithmetic, class Backend, expression_template_option ExpressionTemplates>
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
operator >= (const Arithmetic& a, const number<Backend, ExpressionTemplates>& b)
{
using default_ops::eval_gt;
if(detail::is_unordered_comparison(a, b)) return false;
return !eval_gt(b.backend(), number<Backend, ExpressionTemplates>::canonical_value(a));
}
template <class Arithmetic, class Tag, class A1, class A2, class A3, class A4>
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
operator >= (const Arithmetic& a, const detail::expression<Tag, A1, A2, A3, A4>& b)
{
typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type;
using default_ops::eval_gt;
result_type t(b);
if(detail::is_unordered_comparison(a, t)) return false;
return !eval_gt(t.backend(), result_type::canonical_value(a));
}
template <class Tag, class A1, class A2, class A3, class A4, class Arithmetic>
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
operator >= (const detail::expression<Tag, A1, A2, A3, A4>& a, const Arithmetic& b)
{
typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type;
using default_ops::eval_lt;
result_type t(a);
if(detail::is_unordered_comparison(t, b)) return false;
return !eval_lt(t.backend(), result_type::canonical_value(b));
}
template <class Tag, class A1, class A2, class A3, class A4, class Tagb, class A1b, class A2b, class A3b, class A4b>
inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type>, bool>::type
operator >= (const detail::expression<Tag, A1, A2, A3, A4>& a, const detail::expression<Tagb, A1b, A2b, A3b, A4b>& b)
{
using default_ops::eval_lt;
typename detail::expression<Tag, A1, A2, A3, A4>::result_type t(a);
typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type t2(b);
if(detail::is_unordered_comparison(t, t2)) return false;
return !eval_lt(t.backend(), t2.backend());
}
}} // namespaces
#endif // BOOST_MP_COMPARE_HPP

68
libethash-cl/ethash_cl_miner.cpp

@ -36,6 +36,7 @@
#include "ethash_cl_miner_kernel.h"
#define ETHASH_BYTES 32
#define ETHASH_CL_MINIMUM_MEMORY 2000000000
// workaround lame platforms
#if !CL_VERSION_1_2
@ -48,6 +49,9 @@
using namespace std;
// TODO: If at any point we can use libdevcore in here then we should switch to using a LogChannel
#define ETHCL_LOG(_contents) cout << "[OPENCL]:" << _contents << endl
static void add_definition(std::string& source, char const* id, unsigned value)
{
char buf[256];
@ -73,7 +77,7 @@ std::string ethash_cl_miner::platform_info(unsigned _platformId, unsigned _devic
cl::Platform::get(&platforms);
if (platforms.empty())
{
cout << "No OpenCL platforms found." << endl;
ETHCL_LOG("No OpenCL platforms found.");
return std::string();
}
@ -83,7 +87,7 @@ std::string ethash_cl_miner::platform_info(unsigned _platformId, unsigned _devic
platforms[platform_num].getDevices(CL_DEVICE_TYPE_ALL, &devices);
if (devices.empty())
{
cout << "No OpenCL devices found." << endl;
ETHCL_LOG("No OpenCL devices found.");
return std::string();
}
@ -108,7 +112,7 @@ unsigned ethash_cl_miner::get_num_devices(unsigned _platformId)
cl::Platform::get(&platforms);
if (platforms.empty())
{
cout << "No OpenCL platforms found." << endl;
ETHCL_LOG("No OpenCL platforms found.");
return 0;
}
@ -117,12 +121,53 @@ unsigned ethash_cl_miner::get_num_devices(unsigned _platformId)
platforms[platform_num].getDevices(CL_DEVICE_TYPE_ALL, &devices);
if (devices.empty())
{
cout << "No OpenCL devices found." << endl;
ETHCL_LOG("No OpenCL devices found.");
return 0;
}
return devices.size();
}
bool ethash_cl_miner::haveSufficientGPUMemory(unsigned _platformId)
{
std::vector<cl::Platform> platforms;
cl::Platform::get(&platforms);
if (platforms.empty())
{
ETHCL_LOG("No OpenCL platforms found.");
return false;
}
std::vector<cl::Device> devices;
unsigned platform_num = std::min<unsigned>(_platformId, platforms.size() - 1);
platforms[platform_num].getDevices(CL_DEVICE_TYPE_ALL, &devices);
if (devices.empty())
{
ETHCL_LOG("No OpenCL devices found.");
return false;
}
for (cl::Device const& device: devices)
{
cl_ulong result;
device.getInfo(CL_DEVICE_GLOBAL_MEM_SIZE, &result);
if (result >= ETHASH_CL_MINIMUM_MEMORY)
{
ETHCL_LOG(
"Found suitable OpenCL device [" << device.getInfo<CL_DEVICE_NAME>()
<< "] with " << result << " bytes of GPU memory"
);
return true;
}
else
ETHCL_LOG(
"OpenCL device " << device.getInfo<CL_DEVICE_NAME>()
<< " has insufficient GPU memory." << result <<
" bytes of memory found < " << ETHASH_CL_MINIMUM_MEMORY << " bytes of memory required"
);
}
return false;
}
void ethash_cl_miner::finish()
{
if (m_queue())
@ -154,32 +199,31 @@ bool ethash_cl_miner::init(
cl::Platform::get(&platforms);
if (platforms.empty())
{
cout << "No OpenCL platforms found." << endl;
ETHCL_LOG("No OpenCL platforms found.");
return false;
}
// use selected platform
_platformId = std::min<unsigned>(_platformId, platforms.size() - 1);
cout << "Using platform: " << platforms[_platformId].getInfo<CL_PLATFORM_NAME>().c_str() << endl;
ETHCL_LOG("Using platform: " << platforms[_platformId].getInfo<CL_PLATFORM_NAME>().c_str());
// get GPU device of the default platform
std::vector<cl::Device> devices;
platforms[_platformId].getDevices(CL_DEVICE_TYPE_ALL, &devices);
if (devices.empty())
{
cout << "No OpenCL devices found." << endl;
ETHCL_LOG("No OpenCL devices found.");
return false;
}
// use selected device
cl::Device& device = devices[std::min<unsigned>(_deviceId, devices.size() - 1)];
std::string device_version = device.getInfo<CL_DEVICE_VERSION>();
cout << "Using device: " << device.getInfo<CL_DEVICE_NAME>().c_str() << "(" << device_version.c_str() << ")" << endl;
ETHCL_LOG("Using device: " << device.getInfo<CL_DEVICE_NAME>().c_str() << "(" << device_version.c_str() << ")");
if (strncmp("OpenCL 1.0", device_version.c_str(), 10) == 0)
{
cout << "OpenCL 1.0 is not supported." << endl;
ETHCL_LOG("OpenCL 1.0 is not supported.");
return false;
}
if (strncmp("OpenCL 1.1", device_version.c_str(), 10) == 0)
@ -284,9 +328,9 @@ bool ethash_cl_miner::init(
}
catch (cl::Error err)
{
std::cout << err.what() << "(" << err.err() << ")" << std::endl;
ETHCL_LOG(err.what() << "(" << err.err() << ")");
return false;
}
return true;
}

1
libethash-cl/ethash_cl_miner.h

@ -35,6 +35,7 @@ public:
static unsigned get_num_platforms();
static unsigned get_num_devices(unsigned _platformId = 0);
static std::string platform_info(unsigned _platformId = 0, unsigned _deviceId = 0);
static bool haveSufficientGPUMemory(unsigned _platformId = 0);
bool init(
uint8_t const* _dag,

13
libethash/internal.c

@ -364,6 +364,7 @@ static bool ethash_mmap(struct ethash_full* ret, FILE* f)
{
int fd;
char* mmapped_data;
errno = 0;
ret->file = f;
if ((fd = ethash_fileno(ret->file)) == -1) {
return false;
@ -400,38 +401,48 @@ ethash_full_t ethash_full_new_internal(
ret->file_size = (size_t)full_size;
switch (ethash_io_prepare(dirname, seed_hash, &f, (size_t)full_size, false)) {
case ETHASH_IO_FAIL:
// ethash_io_prepare will do all ETHASH_CRITICAL() logging in fail case
goto fail_free_full;
case ETHASH_IO_MEMO_MATCH:
if (!ethash_mmap(ret, f)) {
ETHASH_CRITICAL("mmap failure()");
goto fail_close_file;
}
return ret;
case ETHASH_IO_MEMO_SIZE_MISMATCH:
// if a DAG of same filename but unexpected size is found, silently force new file creation
if (ethash_io_prepare(dirname, seed_hash, &f, (size_t)full_size, true) != ETHASH_IO_MEMO_MISMATCH) {
ETHASH_CRITICAL("Could not recreate DAG file after finding existing DAG with unexpected size.");
goto fail_free_full;
}
// fallthrough to the mismatch case here, DO NOT go through match
case ETHASH_IO_MEMO_MISMATCH:
if (!ethash_mmap(ret, f)) {
ETHASH_CRITICAL("mmap failure()");
goto fail_close_file;
}
break;
}
if (!ethash_compute_full_data(ret->data, full_size, light, callback)) {
ETHASH_CRITICAL("Failure at computing DAG data.");
goto fail_free_full_data;
}
// after the DAG has been filled then we finalize it by writting the magic number at the beginning
if (fseek(f, 0, SEEK_SET) != 0) {
ETHASH_CRITICAL("Could not seek to DAG file start to write magic number.");
goto fail_free_full_data;
}
uint64_t const magic_num = ETHASH_DAG_MAGIC_NUM;
if (fwrite(&magic_num, ETHASH_DAG_MAGIC_NUM_SIZE, 1, f) != 1) {
ETHASH_CRITICAL("Could not write magic number to DAG's beginning.");
goto fail_free_full_data;
}
if (fflush(f) != 0) {// make sure the magic number IS there
ETHASH_CRITICAL("Could not flush memory mapped data to DAG file. Insufficient space?");
goto fail_free_full_data;
}
fflush(f); // make sure the magic number IS there
return ret;
fail_free_full_data:

21
libethash/io.c

@ -21,6 +21,7 @@
#include "io.h"
#include <string.h>
#include <stdio.h>
#include <errno.h>
enum ethash_io_rc ethash_io_prepare(
char const* dirname,
@ -32,15 +33,19 @@ enum ethash_io_rc ethash_io_prepare(
{
char mutable_name[DAG_MUTABLE_NAME_MAX_SIZE];
enum ethash_io_rc ret = ETHASH_IO_FAIL;
// reset errno before io calls
errno = 0;
// assert directory exists
if (!ethash_mkdir(dirname)) {
ETHASH_CRITICAL("Could not create the ethash directory");
goto end;
}
ethash_io_mutable_name(ETHASH_REVISION, &seedhash, mutable_name);
char* tmpfile = ethash_io_create_filename(dirname, mutable_name, strlen(mutable_name));
if (!tmpfile) {
ETHASH_CRITICAL("Could not create the full DAG pathname");
goto end;
}
@ -52,6 +57,7 @@ enum ethash_io_rc ethash_io_prepare(
size_t found_size;
if (!ethash_file_size(f, &found_size)) {
fclose(f);
ETHASH_CRITICAL("Could not query size of DAG file: \"%s\"", tmpfile);
goto free_memo;
}
if (file_size != found_size - ETHASH_DAG_MAGIC_NUM_SIZE) {
@ -64,6 +70,7 @@ enum ethash_io_rc ethash_io_prepare(
if (fread(&magic_num, ETHASH_DAG_MAGIC_NUM_SIZE, 1, f) != 1) {
// I/O error
fclose(f);
ETHASH_CRITICAL("Could not read from DAG file: \"%s\"", tmpfile);
ret = ETHASH_IO_MEMO_SIZE_MISMATCH;
goto free_memo;
}
@ -80,15 +87,25 @@ enum ethash_io_rc ethash_io_prepare(
// file does not exist, will need to be created
f = ethash_fopen(tmpfile, "wb+");
if (!f) {
ETHASH_CRITICAL("Could not create DAG file: \"%s\"", tmpfile);
goto free_memo;
}
// make sure it's of the proper size
if (fseek(f, (long int)(file_size + ETHASH_DAG_MAGIC_NUM_SIZE - 1), SEEK_SET) != 0) {
fclose(f);
ETHASH_CRITICAL("Could not seek to the end of DAG file: \"%s\". Insufficient space?", tmpfile);
goto free_memo;
}
if (fputc('\n', f) == EOF) {
fclose(f);
ETHASH_CRITICAL("Could not write in the end of DAG file: \"%s\". Insufficient space?", tmpfile);
goto free_memo;
}
if (fflush(f) != 0) {
fclose(f);
ETHASH_CRITICAL("Could not flush at end of DAG file: \"%s\". Insufficient space?", tmpfile);
goto free_memo;
}
fputc('\n', f);
fflush(f);
ret = ETHASH_IO_MEMO_MISMATCH;
goto set_file;

17
libethash/io.h

@ -54,6 +54,23 @@ enum ethash_io_rc {
#define snprintf(...) sprintf_s(__VA_ARGS__)
#endif
/**
* Logs a critical error in important parts of ethash. Should mostly help
* figure out what kind of problem (I/O, memory e.t.c.) causes a NULL
* ethash_full_t
*/
#ifdef ETHASH_PRINT_CRITICAL_OUTPUT
#define ETHASH_CRITICAL(...) \
do \
{ \
printf("ETHASH CRITICAL ERROR: "__VA_ARGS__); \
printf("\n"); \
fflush(stdout); \
} while (0)
#else
#define ETHASH_CRITICAL(...)
#endif
/**
* Prepares io for ethash
*

5
libethcore/Ethash.cpp

@ -364,6 +364,11 @@ void Ethash::GPUMiner::pause()
stopWorking();
}
bool Ethash::GPUMiner::haveSufficientGPUMemory()
{
return ethash_cl_miner::haveSufficientGPUMemory(s_platformId);
}
std::string Ethash::GPUMiner::platformInfo()
{
return ethash_cl_miner::platform_info(s_platformId, s_deviceId);

2
libethcore/Ethash.h

@ -87,6 +87,7 @@ public:
static unsigned instances() { return s_numInstances > 0 ? s_numInstances : std::thread::hardware_concurrency(); }
static std::string platformInfo();
static bool haveSufficientGPUMemory() { return false; }
static void setDefaultPlatform(unsigned) {}
static void setDefaultDevice(unsigned) {}
static void setNumInstances(unsigned _instances) { s_numInstances = std::min<unsigned>(_instances, std::thread::hardware_concurrency()); }
@ -115,6 +116,7 @@ public:
static unsigned instances() { return s_numInstances > 0 ? s_numInstances : 1; }
static std::string platformInfo();
static bool haveSufficientGPUMemory();
static unsigned getNumDevices();
static void setDefaultPlatform(unsigned _id) { s_platformId = _id; }
static void setDefaultDevice(unsigned _id) { s_deviceId = _id; }

10
libethcore/EthashAux.cpp

@ -137,7 +137,10 @@ EthashAux::FullAllocation::FullAllocation(ethash_light_t _light, ethash_callback
full = ethash_full_new(_light, _cb);
// cdebug << "Called OK.";
if (!full)
BOOST_THROW_EXCEPTION(ExternalFunctionFailure("ethash_full_new()"));
{
clog(DAGChannel) << "DAG Generation Failure. Reason: " << strerror(errno);
BOOST_THROW_EXCEPTION(ExternalFunctionFailure("ethash_full_new"));
}
}
EthashAux::FullAllocation::~FullAllocation()
@ -240,8 +243,9 @@ Ethash::Result EthashAux::eval(BlockInfo const& _header, Nonce const& _nonce)
Ethash::Result EthashAux::eval(h256 const& _seedHash, h256 const& _headerHash, Nonce const& _nonce)
{
if (FullType dag = get()->m_fulls[_seedHash].lock())
return dag->compute(_headerHash, _nonce);
DEV_GUARDED(get()->x_fulls)
if (FullType dag = get()->m_fulls[_seedHash].lock())
return dag->compute(_headerHash, _nonce);
DEV_IF_THROWS(return EthashAux::get()->light(_seedHash)->compute(_headerHash, _nonce))
{
return Ethash::Result{ ~h256(), h256() };

15
libethcore/Exceptions.h

@ -49,27 +49,28 @@ struct FeeTooSmall: virtual dev::Exception {};
struct TooMuchGasUsed: virtual dev::Exception {};
struct ExtraDataTooBig: virtual dev::Exception {};
struct InvalidSignature: virtual dev::Exception {};
class InvalidBlockFormat: virtual public dev::Exception {};
struct InvalidBlockFormat: virtual dev::Exception {};
struct InvalidUnclesHash: virtual dev::Exception {};
struct InvalidUncle: virtual dev::Exception {};
struct TooManyUncles: virtual dev::Exception {};
struct UncleTooOld: virtual dev::Exception {};
class UncleInChain: virtual public dev::Exception {};
struct UncleIsBrother: virtual dev::Exception {};
struct UncleInChain: virtual dev::Exception {};
struct DuplicateUncleNonce: virtual dev::Exception {};
struct InvalidStateRoot: virtual dev::Exception {};
struct InvalidGasUsed: virtual dev::Exception {};
class InvalidTransactionsHash: virtual public dev::Exception {};
struct InvalidTransactionsHash: virtual dev::Exception {};
struct InvalidTransaction: virtual dev::Exception {};
struct InvalidDifficulty: virtual dev::Exception {};
class InvalidGasLimit: virtual public dev::Exception {};
struct InvalidGasLimit: virtual dev::Exception {};
struct InvalidTransactionGasUsed: virtual dev::Exception {};
struct InvalidTransactionsStateRoot: virtual dev::Exception {};
struct InvalidReceiptsStateRoot: virtual dev::Exception {};
struct InvalidTimestamp: virtual dev::Exception {};
struct InvalidLogBloom: virtual dev::Exception {};
class InvalidNonce: virtual public dev::Exception {};
class InvalidBlockHeaderItemCount: virtual public dev::Exception {};
class InvalidBlockNonce: virtual public dev::Exception {};
struct InvalidNonce: virtual dev::Exception {};
struct InvalidBlockHeaderItemCount: virtual dev::Exception {};
struct InvalidBlockNonce: virtual dev::Exception {};
struct InvalidParentHash: virtual dev::Exception {};
struct InvalidNumber: virtual dev::Exception {};
struct InvalidContractAddress: virtual public dev::Exception {};

14
libethereum/BlockQueue.cpp

@ -101,7 +101,7 @@ void BlockQueue::verifierBody()
}
RLP r(&res.second);
for (auto const& uncle : r[2])
for (auto const& uncle: r[2])
{
try
{
@ -418,3 +418,15 @@ void BlockQueue::retryAllUnknown()
m_unknown.clear();
m_moreToVerify.notify_all();
}
std::ostream& dev::eth::operator<<(std::ostream& _out, BlockQueueStatus const& _bqs)
{
_out << "verified: " << _bqs.verified << endl;
_out << "verifying: " << _bqs.verifying << endl;
_out << "unverified: " << _bqs.unverified << endl;
_out << "future: " << _bqs.future << endl;
_out << "unknown: " << _bqs.unknown << endl;
_out << "bad: " << _bqs.bad << endl;
return _out;
}

2
libethereum/BlockQueue.h

@ -136,5 +136,7 @@ private:
bool m_deleting = false; ///< Exit condition for verifiers.
};
std::ostream& operator<<(std::ostream& _out, BlockQueueStatus const& _s);
}
}

8
libethereum/Client.cpp

@ -44,10 +44,11 @@ VersionChecker::VersionChecker(string const& _dbPath):
try
{
auto protocolVersion = (unsigned)status[0];
(void)protocolVersion;
auto minorProtocolVersion = (unsigned)status[1];
auto databaseVersion = (unsigned)status[2];
m_action =
protocolVersion != eth::c_protocolVersion || databaseVersion != c_databaseVersion ?
databaseVersion != c_databaseVersion ?
WithExisting::Kill
: minorProtocolVersion != eth::c_minorProtocolVersion ?
WithExisting::Verify
@ -268,9 +269,11 @@ void Client::killChain()
{
WriteGuard l(x_postMine);
WriteGuard l2(x_preMine);
WriteGuard l3(x_working);
m_preMine = State();
m_postMine = State();
m_working = State();
m_stateDB = OverlayDB();
m_stateDB = State::openDB(Defaults::dbPath(), WithExisting::Kill);
@ -283,6 +286,7 @@ void Client::killChain()
if (auto h = m_host.lock())
h->reset();
startedWorking();
doWork();
startWorking();
@ -422,7 +426,7 @@ ExecutionResult Client::call(Address _dest, bytes const& _data, u256 _gas, u256
temp = m_postMine;
temp.addBalance(_from, _value + _gasPrice * _gas);
Executive e(temp, LastHashes(), 0);
if (!e.call(_dest, _dest, _from, _value, _gasPrice, &_data, _gas, _from))
if (!e.call(_dest, _from, _value, _gasPrice, &_data, _gas))
e.go();
ret = e.executionResult();
}

138
libethereum/EthereumHost.cpp

@ -91,7 +91,7 @@ void EthereumHost::doWork()
bool netChange = ensureInitialised();
auto h = m_chain.currentHash();
// If we've finished our initial sync (including getting all the blocks into the chain so as to reduce invalid transactions), start trading transactions & blocks
if (!isSyncing() && m_chain.isKnown(m_latestBlockSent))
if (isSyncing() && m_chain.isKnown(m_latestBlockSent))
{
if (m_newTransactions)
{
@ -120,7 +120,7 @@ void EthereumHost::maintainTransactions()
for (auto const& i: ts)
{
bool unsent = !m_transactionsSent.count(i.first);
for (auto const& p: randomSelection(0, [&](EthereumPeer* p) { return p->m_requireTransactions || (unsent && !p->m_knownTransactions.count(i.first)); }).second)
for (auto const& p: get<1>(randomSelection(0, [&](EthereumPeer* p) { return p->m_requireTransactions || (unsent && !p->m_knownTransactions.count(i.first)); })))
peerTransactions[p].push_back(i.first);
}
for (auto const& t: ts)
@ -165,24 +165,32 @@ void EthereumHost::forEachPeerPtr(std::function<void(std::shared_ptr<EthereumPee
_f(s.first->cap<EthereumPeer>(c_oldProtocolVersion));
}
pair<vector<shared_ptr<EthereumPeer>>, vector<shared_ptr<EthereumPeer>>> EthereumHost::randomSelection(unsigned _percent, std::function<bool(EthereumPeer*)> const& _allow)
tuple<vector<shared_ptr<EthereumPeer>>, vector<shared_ptr<EthereumPeer>>, vector<shared_ptr<Session>>> EthereumHost::randomSelection(unsigned _percent, std::function<bool(EthereumPeer*)> const& _allow)
{
pair<vector<shared_ptr<EthereumPeer>>, vector<shared_ptr<EthereumPeer>>> ret;
forEachPeerPtr([&](shared_ptr<EthereumPeer> _p)
vector<shared_ptr<EthereumPeer>> chosen;
vector<shared_ptr<EthereumPeer>> allowed;
vector<shared_ptr<Session>> sessions;
auto const& ps = peerSessions();
allowed.reserve(ps.size());
for (auto const& j: ps)
{
if (_p && _allow(_p.get()))
ret.second.push_back(_p);
});
auto pp = j.first->cap<EthereumPeer>();
if (_allow(pp.get()))
{
allowed.push_back(move(pp));
sessions.push_back(move(j.first));
}
}
size_t size = (ret.second.size() * _percent + 99) / 100;
ret.second.reserve(size);
for (unsigned i = size; i-- && ret.second.size();)
chosen.reserve((ps.size() * _percent + 99) / 100);
for (unsigned i = (ps.size() * _percent + 99) / 100; i-- && allowed.size();)
{
unsigned n = rand() % ret.second.size();
ret.first.push_back(std::move(ret.second[n]));
ret.second.erase(ret.second.begin() + n);
unsigned n = rand() % allowed.size();
chosen.push_back(std::move(allowed[n]));
allowed.erase(allowed.begin() + n);
}
return ret;
return make_tuple(move(chosen), move(allowed), move(sessions));
}
void EthereumHost::maintainBlocks(h256 const& _currentHash)
@ -200,7 +208,7 @@ void EthereumHost::maintainBlocks(h256 const& _currentHash)
h256s blocks = get<0>(m_chain.treeRoute(m_latestBlockSent, _currentHash, false, false, true));
auto s = randomSelection(25, [&](EthereumPeer* p){ DEV_GUARDED(p->x_knownBlocks) return !p->m_knownBlocks.count(_currentHash); return false; });
for (shared_ptr<EthereumPeer> const& p: s.first)
for (shared_ptr<EthereumPeer> const& p: get<0>(s))
for (auto const& b: blocks)
{
RLPStream ts;
@ -210,7 +218,7 @@ void EthereumHost::maintainBlocks(h256 const& _currentHash)
p->sealAndSend(ts);
p->m_knownBlocks.clear();
}
for (shared_ptr<EthereumPeer> const& p: s.second)
for (shared_ptr<EthereumPeer> const& p: get<1>(s))
{
RLPStream ts;
p->prep(ts, NewBlockHashesPacket, blocks.size());
@ -241,7 +249,6 @@ void EthereumHost::onPeerStatus(EthereumPeer* _peer)
_peer->disable("Peer banned for previous bad behaviour.");
else
{
_peer->m_protocolVersion = EthereumHost::c_oldProtocolVersion; //force V60 for now
if (_peer->m_protocolVersion != protocolVersion())
estimatePeerHashes(_peer);
else if (_peer->m_latestBlockNumber > m_chain.number())
@ -283,6 +290,7 @@ void EthereumHost::onPeerHashes(EthereumPeer* _peer, h256s const& _hashes, bool
unsigned knowns = 0;
unsigned unknowns = 0;
h256s neededBlocks;
bool syncByNumber = !m_syncingLatestHash;
for (unsigned i = 0; i < _hashes.size(); ++i)
{
_peer->addRating(1);
@ -290,10 +298,14 @@ void EthereumHost::onPeerHashes(EthereumPeer* _peer, h256s const& _hashes, bool
auto status = m_bq.blockStatus(h);
if (status == QueueStatus::Importing || status == QueueStatus::Ready || m_chain.isKnown(h))
{
clog(NetMessageSummary) << "block hash ready:" << h << ". Start blocks download...";
m_hashes += neededBlocks;
onPeerDoneHashes(_peer, true);
return;
clog(NetMessageSummary) << "Block hash already known:" << h;
if (!syncByNumber)
{
m_hashes += neededBlocks;
clog(NetMessageSummary) << "Start blocks download...";
onPeerDoneHashes(_peer, true);
return;
}
}
else if (status == QueueStatus::Bad)
{
@ -308,65 +320,25 @@ void EthereumHost::onPeerHashes(EthereumPeer* _peer, h256s const& _hashes, bool
}
else
knowns++;
m_syncingLatestHash = h;
}
m_hashes += neededBlocks;
clog(NetMessageSummary) << knowns << "knowns," << unknowns << "unknowns; now at" << m_syncingLatestHash;
if (_complete)
{
m_needSyncBlocks = true;
continueSync(_peer);
if (!syncByNumber)
m_syncingLatestHash = h;
}
else if (m_hashes.size() > _peer->m_expectedHashes)
if (syncByNumber)
{
_peer->disable("Too many hashes");
m_hashes.clear();
m_syncingLatestHash = h256();
continueSync(); ///Try with some other peer, keep the chain
m_man.appendToChain(neededBlocks); // Append to download manager immediatelly
clog(NetMessageSummary) << knowns << "knowns," << unknowns << "unknowns";
}
else
continueSync(_peer); /// Grab next hashes
}
void EthereumHost::onPeerHashes(EthereumPeer* _peer, unsigned /*_index*/, h256s const& _hashes)
{
Guard l(x_sync);
assert(_peer->m_asking == Asking::Nothing);
if (_hashes.empty())
{
onPeerDoneHashes(_peer, true);
return;
m_hashes += neededBlocks; // Append to local list
clog(NetMessageSummary) << knowns << "knowns," << unknowns << "unknowns; now at" << m_syncingLatestHash;
}
unsigned knowns = 0;
unsigned unknowns = 0;
h256s neededBlocks;
for (unsigned i = 0; i < _hashes.size(); ++i)
if (_complete)
{
_peer->addRating(1);
auto h = _hashes[i];
auto status = m_bq.blockStatus(h);
if (status == QueueStatus::Importing || status == QueueStatus::Ready || m_chain.isKnown(h))
{
clog(NetWarn) << "block hash already known:" << h;
}
else if (status == QueueStatus::Bad)
{
clog(NetWarn) << "block hash bad!" << h << ". Bailing...";
_peer->setIdle();
return;
}
else if (status == QueueStatus::Unknown)
{
unknowns++;
neededBlocks.push_back(h);
}
else
knowns++;
m_needSyncBlocks = true;
continueSync(_peer);
}
m_man.appendToChain(neededBlocks);
clog(NetMessageSummary) << knowns << "knowns," << unknowns << "unknowns";
if (m_hashMan.isComplete())
else if (syncByNumber && m_hashMan.isComplete())
{
// Done our chain-get.
m_needSyncHashes = false;
@ -376,8 +348,15 @@ void EthereumHost::onPeerHashes(EthereumPeer* _peer, unsigned /*_index*/, h256s
m_hashMan.reset(m_chain.number() + 1);
continueSync();
}
else if (m_hashes.size() > _peer->m_expectedHashes)
{
_peer->disable("Too many hashes");
m_hashes.clear();
m_syncingLatestHash = h256();
continueSync(); ///Try with some other peer, keep the chain
}
else
continueSync(_peer);
continueSync(_peer); /// Grab next hashes
}
void EthereumHost::onPeerDoneHashes(EthereumPeer* _peer, bool _localChain)
@ -471,7 +450,7 @@ void EthereumHost::onPeerBlocks(EthereumPeer* _peer, RLP const& _r)
void EthereumHost::onPeerNewHashes(EthereumPeer* _peer, h256s const& _hashes)
{
Guard l(x_sync);
if (_peer->m_asking != Asking::Nothing)
if (isSyncing_UNSAFE())
{
clog(NetMessageSummary) << "Ignoring new hashes since we're already downloading.";
return;
@ -483,7 +462,7 @@ void EthereumHost::onPeerNewHashes(EthereumPeer* _peer, h256s const& _hashes)
void EthereumHost::onPeerNewBlock(EthereumPeer* _peer, RLP const& _r)
{
Guard l(x_sync);
if (_peer->m_asking != Asking::Nothing)
if (isSyncing_UNSAFE())
{
clog(NetMessageSummary) << "Ignoring new blocks since we're already downloading.";
return;
@ -525,7 +504,7 @@ void EthereumHost::onPeerNewBlock(EthereumPeer* _peer, RLP const& _r)
_peer->m_totalDifficulty = difficulty;
m_needSyncHashes = true;
m_needSyncBlocks = true;
m_syncingLatestHash = _peer->m_latestHash;
m_syncingLatestHash = h;
sync = true;
}
}
@ -646,9 +625,10 @@ bool EthereumHost::peerShouldGrabChain(EthereumPeer* _peer) const
}
}
bool EthereumHost::isSyncing() const
bool EthereumHost::isSyncing_UNSAFE() const
{
Guard l(x_sync);
/// We need actual peer information here to handle the case when we are the first ever peer on the network to mine.
/// I.e. on a new private network the first node mining has noone to sync with and should start block propogation immediately.
bool syncing = false;
forEachPeer([&](EthereumPeer* _p)
{

9
libethereum/EthereumHost.h

@ -70,8 +70,7 @@ public:
void reset();
DownloadMan const& downloadMan() const { return m_man; }
bool isSyncing() const;
bool isSyncing() const { Guard l(x_sync); return isSyncing_UNSAFE(); }
bool isBanned(p2p::NodeId _id) const { return !!m_banned.count(_id); }
void noteNewTransactions() { m_newTransactions = true; }
@ -82,7 +81,6 @@ public:
void onPeerNewBlock(EthereumPeer* _peer, RLP const& _r); ///< Called by peer once it has new blocks
void onPeerNewHashes(EthereumPeer* _peer, h256s const& _hashes); ///< Called by peer once it has new hashes
void onPeerHashes(EthereumPeer* _peer, h256s const& _hashes); ///< Called by peer once it has another sequential block of hashes during sync
void onPeerHashes(EthereumPeer* _peer, unsigned _index, h256s const& _hashes); ///< Called by peer once it has a new ordered block of hashes starting with a particular number
void onPeerTransactions(EthereumPeer* _peer, RLP const& _r); ///< Called by peer when it has new transactions
DownloadMan& downloadMan() { return m_man; }
@ -90,10 +88,13 @@ public:
BlockChain const& chain() { return m_chain; }
static unsigned const c_oldProtocolVersion;
private:
std::pair<std::vector<std::shared_ptr<EthereumPeer>>, std::vector<std::shared_ptr<EthereumPeer>>> randomSelection(unsigned _percent = 25, std::function<bool(EthereumPeer*)> const& _allow = [](EthereumPeer const*){ return true; });
std::tuple<std::vector<std::shared_ptr<EthereumPeer>>, std::vector<std::shared_ptr<EthereumPeer>>, std::vector<std::shared_ptr<p2p::Session>>> randomSelection(unsigned _percent = 25, std::function<bool(EthereumPeer*)> const& _allow = [](EthereumPeer const*){ return true; });
void forEachPeerPtr(std::function<void(std::shared_ptr<EthereumPeer>)> const& _f) const;
void forEachPeer(std::function<void(EthereumPeer*)> const& _f) const;
bool isSyncing_UNSAFE() const;
/// Sync with the BlockChain. It might contain one of our mined blocks, we might have new candidates from the network.
void doWork();

20
libethereum/EthereumPeer.cpp

@ -40,7 +40,6 @@ EthereumPeer::EthereumPeer(Session* _s, HostCapabilityFace* _h, unsigned _i, Cap
m_hashSub(host()->hashDownloadMan()),
m_peerCapabilityVersion(_cap.second)
{
m_peerCapabilityVersion = EthereumHost::c_oldProtocolVersion;
m_syncHashNumber = host()->chain().number() + 1;
requestStatus();
}
@ -78,7 +77,6 @@ string toString(Asking _a)
return "?";
}
void EthereumPeer::setIdle()
{
m_sub.doneFetch();
@ -88,8 +86,7 @@ void EthereumPeer::setIdle()
void EthereumPeer::requestStatus()
{
if (m_asking != Asking::Nothing)
clog(NetWarn) << "Bad state: requesting state should be the first action";
assert(m_asking == Asking::Nothing);
setAsking(Asking::State);
RLPStream s;
bool latest = m_peerCapabilityVersion == host()->protocolVersion();
@ -106,22 +103,22 @@ void EthereumPeer::requestStatus()
void EthereumPeer::requestHashes()
{
if (m_asking == Asking::Blocks)
return;
assert(m_asking == Asking::Nothing);
m_syncHashNumber = m_hashSub.nextFetch(c_maxHashesAsk);
setAsking(Asking::Hashes);
RLPStream s;
prep(s, GetBlockHashesByNumberPacket, 2) << m_syncHashNumber << c_maxHashesAsk;
clog(NetMessageDetail) << "Requesting block hashes for numbers " << m_syncHashNumber << "-" << m_syncHashNumber + c_maxHashesAsk - 1;
sealAndSend(s);
}
void EthereumPeer::requestHashes(h256 const& _lastHash)
{
if (m_asking == Asking::Blocks)
return;
assert(m_asking == Asking::Nothing);
setAsking(Asking::Hashes);
RLPStream s;
prep(s, GetBlockHashesPacket, 2) << _lastHash << c_maxHashesAsk;
clog(NetMessageDetail) << "Requesting block hashes staring from " << _lastHash;
sealAndSend(s);
}
@ -212,7 +209,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
u256 number256 = _r[0].toInt<u256>();
unsigned number = (unsigned) number256;
unsigned limit = _r[1].toInt<unsigned>();
clog(NetMessageSummary) << "GetBlockHashesByNumber (" << number << "-" << number + limit << ")";
clog(NetMessageSummary) << "GetBlockHashesByNumber (" << number << "-" << number + limit - 1 << ")";
RLPStream s;
if (number <= host()->chain().number())
{
@ -248,11 +245,8 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
m_hashSub.noteHash(m_syncHashNumber + i, 1);
}
if (m_protocolVersion == host()->protocolVersion())
host()->onPeerHashes(this, m_syncHashNumber, hashes); // V61+, report hashes by number
else
host()->onPeerHashes(this, hashes);
m_syncHashNumber += itemCount;
host()->onPeerHashes(this, hashes);
break;
}
case GetBlocksPacket:

100
libethereum/Executive.cpp

@ -32,6 +32,7 @@ using namespace dev;
using namespace dev::eth;
const char* VMTraceChannel::name() { return "EVM"; }
const char* ExecutiveWarnChannel::name() { return WarnChannel::name(); }
Executive::Executive(State& _s, BlockChain const& _bc, unsigned _level):
m_s(_s),
@ -41,7 +42,7 @@ Executive::Executive(State& _s, BlockChain const& _bc, unsigned _level):
u256 Executive::gasUsed() const
{
return m_t.gas() - m_endGas;
return m_t.gas() - m_gas;
}
ExecutionResult Executive::executionResult() const
@ -63,7 +64,7 @@ void Executive::initialize(Transaction const& _transaction)
u256 startGasUsed = m_s.gasUsed();
if (startGasUsed + (bigint)m_t.gas() > m_s.m_currentBlock.gasLimit)
{
clog(StateDetail) << "Too much gas used in this block: Require <" << (m_s.m_currentBlock.gasLimit - startGasUsed) << " Got" << m_t.gas();
clog(ExecutiveWarnChannel) << "Too much gas used in this block: Require <" << (m_s.m_currentBlock.gasLimit - startGasUsed) << " Got" << m_t.gas();
m_excepted = TransactionException::BlockGasLimitReached;
BOOST_THROW_EXCEPTION(BlockGasLimitReached() << RequirementError((bigint)(m_s.m_currentBlock.gasLimit - startGasUsed), (bigint)m_t.gas()));
}
@ -71,7 +72,7 @@ void Executive::initialize(Transaction const& _transaction)
// Check gas cost is enough.
if (!m_t.checkPayment())
{
clog(StateDetail) << "Not enough gas to pay for the transaction: Require >" << m_t.gasRequired() << " Got" << m_t.gas();
clog(ExecutiveWarnChannel) << "Not enough gas to pay for the transaction: Require >" << m_t.gasRequired() << " Got" << m_t.gas();
m_excepted = TransactionException::OutOfGas;
BOOST_THROW_EXCEPTION(OutOfGasBase() << RequirementError(m_t.gasRequired(), (bigint)m_t.gas()));
}
@ -84,13 +85,13 @@ void Executive::initialize(Transaction const& _transaction)
}
catch (...)
{
clog(StateDetail) << "Invalid Signature";
clog(ExecutiveWarnChannel) << "Invalid Signature";
m_excepted = TransactionException::InvalidSignature;
throw;
}
if (m_t.nonce() != nonceReq)
{
clog(StateDetail) << "Invalid Nonce: Require" << nonceReq << " Got" << m_t.nonce();
clog(ExecutiveWarnChannel) << "Invalid Nonce: Require" << nonceReq << " Got" << m_t.nonce();
m_excepted = TransactionException::InvalidNonce;
BOOST_THROW_EXCEPTION(InvalidNonce() << RequirementError((bigint)nonceReq, (bigint)m_t.nonce()));
}
@ -100,7 +101,7 @@ void Executive::initialize(Transaction const& _transaction)
m_totalCost = m_t.value() + m_gasCost;
if (m_s.balance(m_t.sender()) < m_totalCost)
{
clog(StateDetail) << "Not enough cash: Require >" << m_totalCost << " Got" << m_s.balance(m_t.sender());
clog(ExecutiveWarnChannel) << "Not enough cash: Require >" << m_totalCost << " Got" << m_s.balance(m_t.sender());
m_excepted = TransactionException::NotEnoughCash;
BOOST_THROW_EXCEPTION(NotEnoughCash() << RequirementError(m_totalCost, (bigint)m_s.balance(m_t.sender())));
}
@ -120,75 +121,45 @@ bool Executive::execute()
if (m_t.isCreation())
return create(m_t.sender(), m_t.value(), m_t.gasPrice(), m_t.gas() - (u256)m_t.gasRequired(), &m_t.data(), m_t.sender());
else
return call(m_t.receiveAddress(), m_t.receiveAddress(), m_t.sender(), m_t.value(), m_t.gasPrice(), bytesConstRef(&m_t.data()), m_t.gas() - (u256)m_t.gasRequired(), m_t.sender());
return call(m_t.receiveAddress(), m_t.sender(), m_t.value(), m_t.gasPrice(), bytesConstRef(&m_t.data()), m_t.gas() - (u256)m_t.gasRequired());
}
bool Executive::call(Address _receiveAddress, Address _codeAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256 _gas, Address _originAddress)
bool Executive::call(Address _receiveAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256 _gas)
{
m_isCreation = false;
// cnote << "Transferring" << formatBalance(_value) << "to receiver.";
auto it = !(_codeAddress & ~h160(0xffffffff)) ? precompiled().find((unsigned)(u160)_codeAddress) : precompiled().end();
if (it != precompiled().end())
{
bigint g = it->second.gas(_data);
if (_gas < g)
{
m_endGas = 0;
m_excepted = TransactionException::OutOfGasBase;
// Bail from exception.
return true; // true actually means "all finished - nothing more to be done regarding go().
}
else
{
m_endGas = (u256)(_gas - g);
m_precompiledOut = it->second.exec(_data);
m_out = &m_precompiledOut;
}
}
else if (m_s.addressHasCode(_codeAddress))
{
m_vm = VMFactory::create(_gas);
bytes const& c = m_s.code(_codeAddress);
m_ext = make_shared<ExtVM>(m_s, m_lastHashes, _receiveAddress, _senderAddress, _originAddress, _value, _gasPrice, _data, &c, m_depth);
}
else
m_endGas = _gas;
m_s.transferBalance(_senderAddress, _receiveAddress, _value);
return !m_ext;
CallParameters params{_senderAddress, _receiveAddress, _receiveAddress, _gas, _value, _data, {}, {}};
return call(params, _gasPrice, _senderAddress);
}
bool Executive::call(CallParameters const& _p, u256 const& _gasPrice, Address const& _origin)
{
m_isCreation = false;
// cnote << "Transferring" << formatBalance(_value) << "to receiver.";
auto it = !(_p.codeAddress & ~h160(0xffffffff)) ? precompiled().find((unsigned)(u160)_p.codeAddress) : precompiled().end();
if (it != precompiled().end())
{
bigint g = it->second.gas(_p.data);
if (_p.gas < g)
{
m_endGas = 0;
m_excepted = TransactionException::OutOfGasBase;
// Bail from exception.
return true; // true actually means "all finished - nothing more to be done regarding go().
}
else
{
m_endGas = (u256)(_p.gas - g);
m_gas = (u256)(_p.gas - g);
m_precompiledOut = it->second.exec(_p.data);
m_out = &m_precompiledOut;
}
}
else if (m_s.addressHasCode(_p.codeAddress))
else
{
m_vm = VMFactory::create(_p.gas);
bytes const& c = m_s.code(_p.codeAddress);
m_ext = make_shared<ExtVM>(m_s, m_lastHashes, _p.receiveAddress, _p.senderAddress, _origin, _p.value, _gasPrice, _p.data, &c, m_depth);
m_gas = _p.gas;
if (m_s.addressHasCode(_p.codeAddress))
{
m_vm = VMFactory::create();
bytes const& c = m_s.code(_p.codeAddress);
m_ext = make_shared<ExtVM>(m_s, m_lastHashes, _p.receiveAddress, _p.senderAddress, _origin, _p.value, _gasPrice, _p.data, &c, m_depth);
}
}
else
m_endGas = _p.gas;
m_s.transferBalance(_p.senderAddress, _p.receiveAddress, _p.value);
@ -202,11 +173,12 @@ bool Executive::create(Address _sender, u256 _endowment, u256 _gasPrice, u256 _g
// We can allow for the reverted state (i.e. that with which m_ext is constructed) to contain the m_newAddress, since
// we delete it explicitly if we decide we need to revert.
m_newAddress = right160(sha3(rlpList(_sender, m_s.transactionsFrom(_sender) - 1)));
m_gas = _gas;
// Execute _init.
if (!_init.empty())
{
m_vm = VMFactory::create(_gas);
m_vm = VMFactory::create();
m_ext = make_shared<ExtVM>(m_s, m_lastHashes, m_newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _init, m_depth);
}
@ -214,17 +186,14 @@ bool Executive::create(Address _sender, u256 _endowment, u256 _gasPrice, u256 _g
m_s.transferBalance(_sender, m_newAddress, _endowment);
if (_init.empty())
{
m_s.m_cache[m_newAddress].setCode({});
m_endGas = _gas;
}
return !m_ext;
}
OnOpFunc Executive::simpleTrace()
{
return [](uint64_t steps, Instruction inst, bigint newMemSize, bigint gasCost, VM* voidVM, ExtVMFace const* voidExt)
return [](uint64_t steps, Instruction inst, bigint newMemSize, bigint gasCost, bigint gas, VM* voidVM, ExtVMFace const* voidExt)
{
ExtVM const& ext = *static_cast<ExtVM const*>(voidExt);
VM& vm = *voidVM;
@ -238,13 +207,13 @@ OnOpFunc Executive::simpleTrace()
for (auto const& i: ext.state().storage(ext.myAddress))
o << showbase << hex << i.first << ": " << i.second << endl;
dev::LogOutputStream<VMTraceChannel, false>() << o.str();
dev::LogOutputStream<VMTraceChannel, false>() << " < " << dec << ext.depth << " : " << ext.myAddress << " : #" << steps << " : " << hex << setw(4) << setfill('0') << vm.curPC() << " : " << instructionInfo(inst).name << " : " << dec << vm.gas() << " : -" << dec << gasCost << " : " << newMemSize << "x32" << " >";
dev::LogOutputStream<VMTraceChannel, false>() << " < " << dec << ext.depth << " : " << ext.myAddress << " : #" << steps << " : " << hex << setw(4) << setfill('0') << vm.curPC() << " : " << instructionInfo(inst).name << " : " << dec << gas << " : -" << dec << gasCost << " : " << newMemSize << "x32" << " >";
};
}
OnOpFunc Executive::standardTrace(ostream& o_output)
{
return [&](uint64_t steps, Instruction inst, bigint newMemSize, bigint gasCost, VM* voidVM, ExtVMFace const* voidExt)
return [&](uint64_t steps, Instruction inst, bigint newMemSize, bigint gasCost, bigint gas, VM* voidVM, ExtVMFace const* voidExt)
{
ExtVM const& ext = *static_cast<ExtVM const*>(voidExt);
VM& vm = *voidVM;
@ -256,7 +225,7 @@ OnOpFunc Executive::standardTrace(ostream& o_output)
o_output << " STORAGE" << endl;
for (auto const& i: ext.state().storage(ext.myAddress))
o_output << showbase << hex << i.first << ": " << i.second << endl;
o_output << " < " << dec << ext.depth << " : " << ext.myAddress << " : #" << steps << " : " << hex << setw(4) << setfill('0') << vm.curPC() << " : " << instructionInfo(inst).name << " : " << dec << vm.gas() << " : -" << dec << gasCost << " : " << newMemSize << "x32" << " >";
o_output << " < " << dec << ext.depth << " : " << ext.myAddress << " : #" << steps << " : " << hex << setw(4) << setfill('0') << vm.curPC() << " : " << instructionInfo(inst).name << " : " << dec << gas << " : -" << dec << gasCost << " : " << newMemSize << "x32" << " >";
};
}
@ -269,17 +238,16 @@ bool Executive::go(OnOpFunc const& _onOp)
#endif
try
{
m_out = m_vm->go(*m_ext, _onOp);
m_endGas = m_vm->gas();
m_out = m_vm->go(m_gas, *m_ext, _onOp);
if (m_isCreation)
{
m_gasForDeposit = m_endGas;
m_gasForDeposit = m_gas;
m_depositSize = m_out.size();
if (m_out.size() * c_createDataGas <= m_endGas)
if (m_out.size() * c_createDataGas <= m_gas)
{
m_codeDeposit = CodeDeposit::Success;
m_endGas -= m_out.size() * c_createDataGas;
m_gas -= m_out.size() * c_createDataGas;
}
else
{
@ -297,7 +265,7 @@ bool Executive::go(OnOpFunc const& _onOp)
catch (VMException const& _e)
{
clog(StateSafeExceptions) << "Safe VM Exception. " << diagnostic_information(_e);
m_endGas = 0;
m_gas = 0;
m_excepted = toTransactionException(_e);
m_ext->revert();
@ -330,12 +298,12 @@ void Executive::finalize()
// SSTORE refunds...
// must be done before the miner gets the fees.
if (m_ext)
m_endGas += min((m_t.gas() - m_endGas) / 2, m_ext->sub.refunds);
m_gas += min((m_t.gas() - m_gas) / 2, m_ext->sub.refunds);
// cnote << "Refunding" << formatBalance(m_endGas * m_ext->gasPrice) << "to origin (=" << m_endGas << "*" << formatBalance(m_ext->gasPrice) << ")";
m_s.addBalance(m_t.sender(), m_endGas * m_t.gasPrice());
m_s.addBalance(m_t.sender(), m_gas * m_t.gasPrice());
u256 feesEarned = (m_t.gas() - m_endGas) * m_t.gasPrice();
u256 feesEarned = (m_t.gas() - m_gas) * m_t.gasPrice();
m_s.addBalance(m_s.m_currentBlock.coinbaseAddress, feesEarned);
// Suicides...

11
libethereum/Executive.h

@ -36,6 +36,7 @@ class ExtVM;
struct Manifest;
struct VMTraceChannel: public LogChannel { static const char* name(); static const int verbosity = 11; };
struct ExecutiveWarnChannel: public LogChannel { static const char* name(); static const int verbosity = 6; };
/**
* @brief Message-call/contract-creation executor; useful for executing transactions.
@ -94,13 +95,13 @@ public:
bool create(Address _txSender, u256 _endowment, u256 _gasPrice, u256 _gas, bytesConstRef _code, Address _originAddress);
/// Set up the executive for evaluating a bare CALL (message call) operation.
/// @returns false iff go() must be called (and thus a VM execution in required).
bool call(Address _myAddress, Address _codeAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, u256 _gas, Address _originAddress);
bool call(Address _receiveAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, u256 _gas);
bool call(CallParameters const& _cp, u256 const& _gasPrice, Address const& _origin);
/// Finalise an operation through accruing the substate into the parent context.
void accrueSubState(SubState& _parentContext);
/// Executes (or continues execution of) the VM.
/// @returns false iff go() must be called again to finish the transction.
/// @returns false iff go() must be called again to finish the transaction.
bool go(OnOpFunc const& _onOp = OnOpFunc());
/// Operation function for providing a simple trace of the VM execution.
@ -109,8 +110,8 @@ public:
/// Operation function for providing a simple trace of the VM execution.
static OnOpFunc standardTrace(std::ostream& o_output);
/// @returns gas remaining after the transaction/operation.
u256 endGas() const { return m_endGas; }
/// @returns gas remaining after the transaction/operation. Valid after the transaction has been executed.
u256 gas() const { return m_gas; }
/// @returns output data of the transaction/operation.
bytesConstRef out() const { return m_out; }
/// @returns the new address for the created contract in the CREATE operation.
@ -136,7 +137,7 @@ private:
u256 m_gasForDeposit; ///< Amount of gas remaining for the code deposit phase.
CodeDeposit m_codeDeposit = CodeDeposit::None; ///< True if an attempted deposit failed due to lack of gas.
TransactionException m_excepted = TransactionException::None; ///< Details if the VM's execution resulted in an exception.
u256 m_endGas; ///< The final amount of gas for the transaction.
u256 m_gas = 0; ///< The gas for EVM code execution. Initial amount before go() execution, final amount after go() execution.
Transaction m_t; ///< The original transaction. Set by setup().
LogEntries m_logs; ///< The log entries created by this transaction. Set by finalize().

4
libethereum/ExtVM.cpp

@ -34,7 +34,7 @@ bool ExtVM::call(CallParameters& _p)
e.go(_p.onOp);
e.accrueSubState(sub);
}
_p.gas = e.endGas();
_p.gas = e.gas();
e.out().copyTo(_p.out);
return !e.excepted();
@ -51,7 +51,7 @@ h160 ExtVM::create(u256 _endowment, u256& io_gas, bytesConstRef _code, OnOpFunc
e.go(_onOp);
e.accrueSubState(sub);
}
io_gas = e.endGas();
io_gas = e.gas();
return e.newAddress();
}

43
libethereum/State.cpp

@ -592,6 +592,24 @@ string State::vmTrace(bytesConstRef _block, BlockChain const& _bc, ImportRequire
return ss.str();
}
template <class Channel>
class LogOverride
{
public:
LogOverride(bool _value): m_old(g_logOverride.count(&typeid(Channel)) ? (int)g_logOverride[&typeid(Channel)] : c_null) { g_logOverride[&typeid(Channel)] = _value; }
~LogOverride()
{
if (m_old == c_null)
g_logOverride.erase(&typeid(Channel));
else
g_logOverride[&typeid(Channel)] = (bool)m_old;
}
private:
static const int c_null = -1;
int m_old;
};
u256 State::enact(bytesConstRef _block, BlockChain const& _bc, ImportRequirements::value _ir)
{
// m_currentBlock is assumed to be prepopulated and reset.
@ -624,7 +642,19 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, ImportRequirement
unsigned i = 0;
for (auto const& tr: rlp[1])
{
execute(lh, Transaction(tr.data(), CheckTransaction::Everything));
try {
LogOverride<ExecutiveWarnChannel> o(false);
execute(lh, Transaction(tr.data(), CheckTransaction::Everything));
}
catch (...)
{
badBlock(_block, "Invalid transaction");
cwarn << " Transaction Index:" << i;
LogOverride<ExecutiveWarnChannel> o(true);
execute(lh, Transaction(tr.data(), CheckTransaction::Everything));
throw;
}
RLPStream receiptRLP;
m_receipts.back().streamRLP(receiptRLP);
receipts.push_back(receiptRLP.out());
@ -696,6 +726,14 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, ImportRequirement
cwarn << " Block number: " << m_currentBlock.number;
BOOST_THROW_EXCEPTION(UncleTooOld());
}
else if (uncle.number == m_currentBlock.number)
{
badBlock(_block, "Uncle is brother");
cwarn << " Uncle number: " << uncle.number;
cwarn << " Uncle parent number: " << uncleParent.number;
cwarn << " Block number: " << m_currentBlock.number;
BOOST_THROW_EXCEPTION(UncleIsBrother());
}
uncle.verifyParent(uncleParent);
// tdIncrease += uncle.difficulty;
@ -1165,7 +1203,10 @@ ExecutionResult State::execute(LastHashes const& _lh, Transaction const& _t, Per
#endif
if (!e.execute())
#if ETH_VMTRACE
{
(void)_onOp;
e.go(e.simpleTrace());
}
#else
e.go(_onOp);
#endif

21
libethereum/Transaction.cpp

@ -39,6 +39,27 @@ std::ostream& dev::eth::operator<<(std::ostream& _out, ExecutionResult const& _e
return _out;
}
std::string badTransaction(bytesConstRef _tx, string const& _err)
{
stringstream ret;
ret << "========================================================================" << endl;
ret << "== Software Failure " << (_err + string(max<int>(0, 44 - _err.size()), ' ')) << " ==" << endl;
ret << "== Guru Meditation " << sha3(_tx).abridged() << " ==" << endl;
ret << "========================================================================" << endl;
ret << " Transaction: " << toHex(_tx) << endl;
ret << " Transaction RLP: ";
try {
ret << RLP(_tx);
}
catch (Exception& _e)
{
ret << "Invalid: " << _e.what();
}
ret << endl;
return ret.str();
}
TransactionException dev::eth::toTransactionException(VMException const& _e)
{
if (!!dynamic_cast<BadInstruction const*>(&_e))

3
libethereum/Transaction.h

@ -235,5 +235,8 @@ inline std::ostream& operator<<(std::ostream& _out, Transaction const& _t)
return _out;
}
void badTransaction(bytesConstRef _tx, std::string const& _err);
inline void badTransaction(bytes const& _tx, std::string const& _err) { badTransaction(&_tx, _err); }
}
}

2
libevm/ExtVMFace.h

@ -106,7 +106,7 @@ class VM;
using LastHashes = std::vector<h256>;
using OnOpFunc = std::function<void(uint64_t /*steps*/, Instruction /*instr*/, bigint /*newMemSize*/, bigint /*gasCost*/, VM*, ExtVMFace const*)>;
using OnOpFunc = std::function<void(uint64_t /*steps*/, Instruction /*instr*/, bigint /*newMemSize*/, bigint /*gasCost*/, bigint /*gas*/, VM*, ExtVMFace const*)>;
struct CallParameters
{

10
libevm/SmartVM.cpp

@ -41,7 +41,7 @@ namespace
}
}
bytesConstRef SmartVM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
bytesConstRef SmartVM::go(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
{
auto codeHash = sha3(_ext.code);
auto vmKind = VMKind::Interpreter; // default VM
@ -66,11 +66,9 @@ bytesConstRef SmartVM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step
}
// TODO: Selected VM must be kept only because it returns reference to its internal memory.
// VM implementations should be stateless, without gas counter and escaping memory reference.
m_selectedVM = VMFactory::create(vmKind, gas());
auto out = m_selectedVM->go(_ext, _onOp, _steps);
m_gas = m_selectedVM->gas();
return out;
// VM implementations should be stateless, without escaping memory reference.
m_selectedVM = VMFactory::create(vmKind);
return m_selectedVM->go(io_gas, _ext, _onOp, _steps);
}
}

8
libevm/SmartVM.h

@ -31,16 +31,10 @@ namespace eth
class SmartVM: public VMFace
{
public:
SmartVM(u256 const& _gas): m_gas(_gas) {}
virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final;
void reset(u256 const& _gas = 0) noexcept override { m_gas = _gas; }
u256 gas() const noexcept override { return (u256)m_gas; }
virtual bytesConstRef go(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final;
private:
std::unique_ptr<VMFace> m_selectedVM;
bigint m_gas;
};
}

37
libevm/VM.cpp

@ -25,13 +25,6 @@ using namespace std;
using namespace dev;
using namespace dev::eth;
void VM::reset(u256 const& _gas) noexcept
{
m_gas = _gas;
m_curPC = 0;
m_jumpDests.clear();
}
struct InstructionMetric
{
int gasPriceTier;
@ -52,8 +45,12 @@ static array<InstructionMetric, 256> metrics()
return s_ret;
}
bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
bytesConstRef VM::go(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
{
// Reset leftovers from possible previous run
m_curPC = 0;
m_jumpDests.clear();
m_stack.reserve((unsigned)c_stackLimit);
unique_ptr<CallParameters> callParams;
@ -100,7 +97,7 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
auto onOperation = [&]()
{
if (_onOp)
_onOp(osteps - _steps - 1, inst, newTempSize > m_temp.size() ? (newTempSize - m_temp.size()) / 32 : bigint(0), runGas, this, &_ext);
_onOp(osteps - _steps - 1, inst, newTempSize > m_temp.size() ? (newTempSize - m_temp.size()) / 32 : bigint(0), runGas, io_gas, this, &_ext);
};
switch (inst)
@ -198,17 +195,11 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
runGas += c_copyGas * ((copySize + 31) / 32);
onOperation();
// if (_onOp)
// _onOp(osteps - _steps - 1, inst, newTempSize > m_temp.size() ? (newTempSize - m_temp.size()) / 32 : bigint(0), runGas, this, &_ext);
if (m_gas < runGas)
{
// Out of gas!
m_gas = 0;
if (io_gas < runGas)
BOOST_THROW_EXCEPTION(OutOfGas());
}
m_gas -= runGas;
io_gas -= (u256)runGas;
if (newTempSize > m_temp.size())
m_temp.resize((size_t)newTempSize);
@ -567,7 +558,7 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
m_stack.push_back(m_temp.size());
break;
case Instruction::GAS:
m_stack.push_back((u256)m_gas);
m_stack.push_back(io_gas);
break;
case Instruction::JUMPDEST:
break;
@ -616,11 +607,7 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
m_stack.pop_back();
if (_ext.balance(_ext.myAddress) >= endowment && _ext.depth < 1024)
{
u256 g(m_gas);
m_stack.push_back((u160)_ext.create(endowment, g, bytesConstRef(m_temp.data() + initOff, initSize), _onOp));
m_gas = g;
}
m_stack.push_back((u160)_ext.create(endowment, io_gas, bytesConstRef(m_temp.data() + initOff, initSize), _onOp));
else
m_stack.push_back(0);
break;
@ -661,7 +648,7 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
else
m_stack.push_back(0);
m_gas += callParams->gas;
io_gas += callParams->gas;
break;
}
case Instruction::RETURN:
@ -670,7 +657,6 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
m_stack.pop_back();
unsigned s = (unsigned)m_stack.back();
m_stack.pop_back();
return bytesConstRef(m_temp.data() + b, s);
}
case Instruction::SUICIDE:
@ -683,6 +669,7 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
return bytesConstRef();
}
}
if (_steps == (uint64_t)-1)
BOOST_THROW_EXCEPTION(StepsDone());
return bytesConstRef();

15
libevm/VM.h

@ -52,31 +52,22 @@ inline u256 fromAddress(Address _a)
class VM: public VMFace
{
public:
virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final;
void require(u256 _n, u256 _d) { if (m_stack.size() < _n) { if (m_onFail) m_onFail(); BOOST_THROW_EXCEPTION(StackUnderflow() << RequirementError((bigint)_n, (bigint)m_stack.size())); } if (m_stack.size() - _n + _d > c_stackLimit) { if (m_onFail) m_onFail(); BOOST_THROW_EXCEPTION(OutOfStack() << RequirementError((bigint)(_d - _n), (bigint)m_stack.size())); } }
void requireMem(unsigned _n) { if (m_temp.size() < _n) { m_temp.resize(_n); } }
virtual bytesConstRef go(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final;
u256 curPC() const { return m_curPC; }
bytes const& memory() const { return m_temp; }
u256s const& stack() const { return m_stack; }
void reset(u256 const& _gas = 0) noexcept override;
u256 gas() const noexcept override { return (u256)m_gas; }
private:
friend class VMFactory;
/// Construct VM object.
explicit VM(u256 _gas): m_gas(_gas) {}
void require(u256 _n, u256 _d) { if (m_stack.size() < _n) { if (m_onFail) m_onFail(); BOOST_THROW_EXCEPTION(StackUnderflow() << RequirementError((bigint)_n, (bigint)m_stack.size())); } if (m_stack.size() - _n + _d > c_stackLimit) { if (m_onFail) m_onFail(); BOOST_THROW_EXCEPTION(OutOfStack() << RequirementError((bigint)(_d - _n), (bigint)m_stack.size())); } }
void requireMem(unsigned _n) { if (m_temp.size() < _n) { m_temp.resize(_n); } }
u256 m_curPC = 0;
bytes m_temp;
u256s m_stack;
std::vector<uint64_t> m_jumpDests;
std::function<void()> m_onFail;
bigint m_gas = 0;
};
}

5
libevm/VMFace.h

@ -43,10 +43,7 @@ public:
VMFace(VMFace const&) = delete;
VMFace& operator=(VMFace const&) = delete;
virtual void reset(u256 const& _gas = 0) noexcept = 0;
virtual u256 gas() const noexcept = 0;
virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) = 0;
virtual bytesConstRef go(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) = 0;
};
}

14
libevm/VMFactory.cpp

@ -38,27 +38,27 @@ void VMFactory::setKind(VMKind _kind)
g_kind = _kind;
}
std::unique_ptr<VMFace> VMFactory::create(u256 _gas)
std::unique_ptr<VMFace> VMFactory::create()
{
return create(g_kind, _gas);
return create(g_kind);
}
std::unique_ptr<VMFace> VMFactory::create(VMKind _kind, u256 _gas)
std::unique_ptr<VMFace> VMFactory::create(VMKind _kind)
{
#if ETH_EVMJIT
switch (_kind)
{
default:
case VMKind::Interpreter:
return std::unique_ptr<VMFace>(new VM(_gas));
return std::unique_ptr<VMFace>(new VM);
case VMKind::JIT:
return std::unique_ptr<VMFace>(new JitVM(_gas));
return std::unique_ptr<VMFace>(new JitVM);
case VMKind::Smart:
return std::unique_ptr<VMFace>(new SmartVM(_gas));
return std::unique_ptr<VMFace>(new SmartVM);
}
#else
asserts(_kind == VMKind::Interpreter && "JIT disabled in build configuration");
return std::unique_ptr<VMFace>(new VM(_gas));
return std::unique_ptr<VMFace>(new VM);
#endif
}

4
libevm/VMFactory.h

@ -36,10 +36,10 @@ public:
VMFactory() = delete;
/// Creates a VM instance of global kind (controlled by setKind() function).
static std::unique_ptr<VMFace> create(u256 _gas);
static std::unique_ptr<VMFace> create();
/// Creates a VM instance of kind provided.
static std::unique_ptr<VMFace> create(VMKind _kind, u256 _gas);
static std::unique_ptr<VMFace> create(VMKind _kind);
/// Set global VM kind
static void setKind(VMKind _kind);

42
libevmasm/Assembly.cpp

@ -127,7 +127,10 @@ ostream& Assembly::streamAsm(ostream& _out, string const& _prefix, StringMap con
_out << " PUSH \"" << m_strings.at((h256)i.data()) << "\"";
break;
case PushTag:
_out << " PUSH [tag" << dec << i.data() << "]";
if (i.data() == 0)
_out << " PUSH [ErrorTag]";
else
_out << " PUSH [tag" << dec << i.data() << "]";
break;
case PushSub:
_out << " PUSH [$" << h256(i.data()).abridged() << "]";
@ -207,8 +210,12 @@ Json::Value Assembly::streamAsmJson(ostream& _out, StringMap const& _sourceCodes
createJsonValue("PUSH tag", i.getLocation().start, i.getLocation().end, m_strings.at((h256)i.data())));
break;
case PushTag:
collection.append(
createJsonValue("PUSH [tag]", i.getLocation().start, i.getLocation().end, string(i.data())));
if (i.data() == 0)
collection.append(
createJsonValue("PUSH [ErrorTag]", i.getLocation().start, i.getLocation().end, ""));
else
collection.append(
createJsonValue("PUSH [tag]", i.getLocation().start, i.getLocation().end, string(i.data())));
break;
case PushSub:
collection.append(
@ -226,7 +233,7 @@ Json::Value Assembly::streamAsmJson(ostream& _out, StringMap const& _sourceCodes
collection.append(
createJsonValue("tag", i.getLocation().start, i.getLocation().end, string(i.data())));
collection.append(
createJsonValue("JUMDEST", i.getLocation().start, i.getLocation().end));
createJsonValue("JUMPDEST", i.getLocation().start, i.getLocation().end));
break;
case PushData:
collection.append(createJsonValue("PUSH data", i.getLocation().start, i.getLocation().end, toStringInHex(i.data())));
@ -307,6 +314,11 @@ Assembly& Assembly::optimise(bool _enable)
count = 0;
copt << "Performing optimisation...";
// This only modifies PushTags, we have to run again to actually remove code.
BlockDeduplicator dedup(m_items);
if (dedup.deduplicate())
count++;
{
ControlFlowGraph cfg(m_items);
AssemblyItems optimisedItems;
@ -349,11 +361,6 @@ Assembly& Assembly::optimise(bool _enable)
m_items = move(optimisedItems);
count++;
}
// This only modifies PushTags, we have to run again to actually remove code.
BlockDeduplicator dedup(m_items);
if (dedup.deduplicate())
count++;
}
}
@ -387,6 +394,11 @@ bytes Assembly::assemble() const
// m_data must not change from here on
for (AssemblyItem const& i: m_items)
{
// store position of the invalid jump destination
if (i.type() != Tag && tagPos[0] == 0)
tagPos[0] = ret.size();
switch (i.type())
{
case Operation:
@ -448,17 +460,23 @@ bytes Assembly::assemble() const
}
case Tag:
tagPos[(unsigned)i.data()] = ret.size();
assertThrow(i.data() != 0, AssemblyException, "");
ret.push_back((byte)Instruction::JUMPDEST);
break;
default:
BOOST_THROW_EXCEPTION(InvalidOpcode());
}
}
for (auto const& i: tagRef)
{
bytesRef r(ret.data() + i.first, bytesPerTag);
//@todo in the failure case, we could use the position of the invalid jumpdest
toBigEndian(i.second < tagPos.size() ? tagPos[i.second] : (1 << (8 * bytesPerTag)) - 1, r);
auto tag = i.second;
if (tag >= tagPos.size())
tag = 0;
if (tag == 0)
assertThrow(tagPos[tag] != 0, AssemblyException, "");
toBigEndian(tagPos[tag], r);
}
if (!m_data.empty())

6
libevmasm/Assembly.h

@ -67,6 +67,8 @@ public:
AssemblyItem appendJumpI() { auto ret = append(newPushTag()); append(Instruction::JUMPI); return ret; }
AssemblyItem appendJump(AssemblyItem const& _tag) { auto ret = append(_tag.pushTag()); append(Instruction::JUMP); return ret; }
AssemblyItem appendJumpI(AssemblyItem const& _tag) { auto ret = append(_tag.pushTag()); append(Instruction::JUMPI); return ret; }
AssemblyItem errorTag() { return AssemblyItem(PushTag, 0); }
template <class T> Assembly& operator<<(T const& _d) { append(_d); return *this; }
AssemblyItems const& getItems() const { return m_items; }
AssemblyItem const& back() const { return m_items.back(); }
@ -97,7 +99,6 @@ public:
const StringMap &_sourceCodes = StringMap(),
bool _inJsonFormat = false
) const;
protected:
std::string getLocationFromSources(StringMap const& _sourceCodes, SourceLocation const& _location) const;
void donePath() { if (m_totalDeposit != INT_MAX && m_totalDeposit != m_deposit) BOOST_THROW_EXCEPTION(InvalidDeposit()); }
@ -109,7 +110,8 @@ private:
Json::Value createJsonValue(std::string _name, int _begin, int _end, std::string _value = std::string(), std::string _jumpType = std::string()) const;
protected:
unsigned m_usedTags = 0;
// 0 is reserved for exception
unsigned m_usedTags = 1;
AssemblyItems m_items;
mutable std::map<h256, bytes> m_data;
std::vector<Assembly> m_subs;

2
libevmasm/AssemblyItem.h

@ -65,7 +65,7 @@ public:
/// @returns the instruction of this item (only valid if type() == Operation)
Instruction instruction() const { return Instruction(byte(m_data)); }
/// @returns true iff the type and data of the items are equal.
/// @returns true if the type and data of the items are equal.
bool operator==(AssemblyItem const& _other) const { return m_type == _other.m_type && m_data == _other.m_data; }
bool operator!=(AssemblyItem const& _other) const { return !operator==(_other); }
/// Less-than operator compatible with operator==.

75
libevmasm/BlockDeduplicator.cpp

@ -35,13 +35,33 @@ bool BlockDeduplicator::deduplicate()
{
// Compares indices based on the suffix that starts there, ignoring tags and stopping at
// opcodes that stop the control flow.
// Virtual tag that signifies "the current block" and which is used to optimise loops.
// We abort if this virtual tag actually exists.
AssemblyItem pushSelf(PushTag, u256(-4));
if (
std::count(m_items.cbegin(), m_items.cend(), pushSelf.tag()) ||
std::count(m_items.cbegin(), m_items.cend(), pushSelf.pushTag())
)
return false;
function<bool(size_t, size_t)> comparator = [&](size_t _i, size_t _j)
{
if (_i == _j)
return false;
BlockIterator first(m_items.begin() + _i, m_items.end());
BlockIterator second(m_items.begin() + _j, m_items.end());
// To compare recursive loops, we have to already unify PushTag opcodes of the
// block's own tag.
AssemblyItem pushFirstTag(pushSelf);
AssemblyItem pushSecondTag(pushSelf);
if (_i < m_items.size() && m_items.at(_i).type() == Tag)
pushFirstTag = m_items.at(_i).pushTag();
if (_j < m_items.size() && m_items.at(_j).type() == Tag)
pushSecondTag = m_items.at(_j).pushTag();
BlockIterator first(m_items.begin() + _i, m_items.end(), &pushFirstTag, &pushSelf);
BlockIterator second(m_items.begin() + _j, m_items.end(), &pushSecondTag, &pushSelf);
BlockIterator end(m_items.end(), m_items.end());
if (first != end && (*first).type() == Tag)
@ -52,27 +72,34 @@ bool BlockDeduplicator::deduplicate()
return std::lexicographical_compare(first, end, second, end);
};
set<size_t, function<bool(size_t, size_t)>> blocksSeen(comparator);
map<u256, u256> tagReplacement;
for (size_t i = 0; i < m_items.size(); ++i)
size_t iterations = 0;
for (; ; ++iterations)
{
if (m_items.at(i).type() != Tag)
continue;
auto it = blocksSeen.find(i);
if (it == blocksSeen.end())
blocksSeen.insert(i);
else
tagReplacement[m_items.at(i).data()] = m_items.at(*it).data();
}
bool ret = false;
for (AssemblyItem& item: m_items)
if (item.type() == PushTag && tagReplacement.count(item.data()))
//@todo this should probably be optimized.
set<size_t, function<bool(size_t, size_t)>> blocksSeen(comparator);
map<u256, u256> tagReplacement;
for (size_t i = 0; i < m_items.size(); ++i)
{
ret = true;
item.setData(tagReplacement.at(item.data()));
if (m_items.at(i).type() != Tag)
continue;
auto it = blocksSeen.find(i);
if (it == blocksSeen.end())
blocksSeen.insert(i);
else
tagReplacement[m_items.at(i).data()] = m_items.at(*it).data();
}
return ret;
bool changed = false;
for (AssemblyItem& item: m_items)
if (item.type() == PushTag && tagReplacement.count(item.data()))
{
changed = true;
item.setData(tagReplacement.at(item.data()));
}
if (!changed)
break;
}
return iterations > 0;
}
BlockDeduplicator::BlockIterator& BlockDeduplicator::BlockIterator::operator++()
@ -89,3 +116,11 @@ BlockDeduplicator::BlockIterator& BlockDeduplicator::BlockIterator::operator++()
}
return *this;
}
AssemblyItem const& BlockDeduplicator::BlockIterator::operator*() const
{
if (replaceItem && replaceWith && *it == *replaceItem)
return *replaceWith;
else
return *it;
}

16
libevmasm/BlockDeduplicator.h

@ -47,19 +47,27 @@ public:
bool deduplicate();
private:
/// Iterator that skips tags skips to the end if (all branches of) the control
/// Iterator that skips tags and skips to the end if (all branches of) the control
/// flow does not continue to the next instruction.
/// If the arguments are supplied to the constructor, replaces items on the fly.
struct BlockIterator: std::iterator<std::forward_iterator_tag, AssemblyItem const>
{
public:
BlockIterator(AssemblyItems::const_iterator _it, AssemblyItems::const_iterator _end):
it(_it), end(_end) { }
BlockIterator(
AssemblyItems::const_iterator _it,
AssemblyItems::const_iterator _end,
AssemblyItem const* _replaceItem = nullptr,
AssemblyItem const* _replaceWith = nullptr
):
it(_it), end(_end), replaceItem(_replaceItem), replaceWith(_replaceWith) {}
BlockIterator& operator++();
bool operator==(BlockIterator const& _other) const { return it == _other.it; }
bool operator!=(BlockIterator const& _other) const { return it != _other.it; }
AssemblyItem const& operator*() const { return *it; }
AssemblyItem const& operator*() const;
AssemblyItems::const_iterator it;
AssemblyItems::const_iterator end;
AssemblyItem const* replaceItem;
AssemblyItem const* replaceWith;
};
AssemblyItems& m_items;

8
libevmasm/ControlFlowGraph.cpp

@ -226,7 +226,10 @@ void ControlFlowGraph::gatherKnowledge()
//@todo we might have to do something like incrementing the sequence number for each JUMPDEST
assertThrow(!!workQueue.back().first, OptimizerException, "");
if (!m_blocks.count(workQueue.back().first))
{
workQueue.pop_back();
continue; // too bad, we do not know the tag, probably an invalid jump
}
BasicBlock& block = m_blocks.at(workQueue.back().first);
KnownStatePointer state = workQueue.back().second;
workQueue.pop_back();
@ -257,10 +260,7 @@ void ControlFlowGraph::gatherKnowledge()
);
state->feedItem(m_items.at(pc++));
if (tags.empty() || std::any_of(tags.begin(), tags.end(), [&](u256 const& _tag)
{
return !m_blocks.count(BlockId(_tag));
}))
if (tags.empty())
{
if (!unknownJumpEncountered)
{

6
libp2p/HostCapability.cpp

@ -27,15 +27,15 @@ using namespace std;
using namespace dev;
using namespace dev::p2p;
std::vector<std::pair<std::shared_ptr<Session>,std::shared_ptr<Peer>>> HostCapabilityFace::peerSessions() const
std::vector<std::pair<std::shared_ptr<Session>, std::shared_ptr<Peer>>> HostCapabilityFace::peerSessions() const
{
return peerSessions(version());
}
std::vector<std::pair<std::shared_ptr<Session>,std::shared_ptr<Peer>>> HostCapabilityFace::peerSessions(u256 const& _version) const
std::vector<std::pair<std::shared_ptr<Session>, std::shared_ptr<Peer>>> HostCapabilityFace::peerSessions(u256 const& _version) const
{
RecursiveGuard l(m_host->x_sessions);
std::vector<std::pair<std::shared_ptr<Session>,std::shared_ptr<Peer>>> ret;
std::vector<std::pair<std::shared_ptr<Session>, std::shared_ptr<Peer>>> ret;
for (auto const& i: m_host->m_sessions)
if (std::shared_ptr<Session> s = i.second.lock())
if (s->m_capabilities.count(std::make_pair(name(), _version)))

4
libp2p/HostCapability.h

@ -45,8 +45,8 @@ public:
Host* host() const { return m_host; }
std::vector<std::pair<std::shared_ptr<Session>,std::shared_ptr<Peer>>> peerSessions() const;
std::vector<std::pair<std::shared_ptr<Session>,std::shared_ptr<Peer>>> peerSessions(u256 const& _version) const;
std::vector<std::pair<std::shared_ptr<Session>, std::shared_ptr<Peer>>> peerSessions() const;
std::vector<std::pair<std::shared_ptr<Session>, std::shared_ptr<Peer>>> peerSessions(u256 const& _version) const;
protected:
virtual std::string name() const = 0;

10
libp2p/Session.cpp

@ -319,10 +319,14 @@ void Session::send(bytes&& _msg)
void Session::write()
{
const bytes& bytes = m_writeQueue[0];
m_io->writeSingleFramePacket(&bytes, m_writeQueue[0]);
bytes const* out;
DEV_GUARDED(x_writeQueue)
{
m_io->writeSingleFramePacket(&m_writeQueue[0], m_writeQueue[0]);
out = &m_writeQueue[0];
}
auto self(shared_from_this());
ba::async_write(m_socket, ba::buffer(bytes), [this, self](boost::system::error_code ec, std::size_t /*length*/)
ba::async_write(m_socket, ba::buffer(*out), [this, self](boost::system::error_code ec, std::size_t /*length*/)
{
ThreadContext tc(info().id.abridged());
ThreadContext tc2(info().clientVersion);

10
libp2p/UDP.h

@ -65,8 +65,8 @@ protected:
*/
struct RLPXDatagramFace: public UDPDatagram
{
static uint32_t futureFromEpoch(std::chrono::seconds _sec) { return std::chrono::duration_cast<std::chrono::seconds>((std::chrono::system_clock::now() + _sec).time_since_epoch()).count(); }
static uint32_t secondsSinceEpoch() { return std::chrono::duration_cast<std::chrono::seconds>((std::chrono::system_clock::now()).time_since_epoch()).count(); }
static uint32_t futureFromEpoch(std::chrono::seconds _sec) { return static_cast<uint32_t>(std::chrono::duration_cast<std::chrono::seconds>((std::chrono::system_clock::now() + _sec).time_since_epoch()).count()); }
static uint32_t secondsSinceEpoch() { return static_cast<uint32_t>(std::chrono::duration_cast<std::chrono::seconds>((std::chrono::system_clock::now()).time_since_epoch()).count()); }
static Public authenticate(bytesConstRef _sig, bytesConstRef _rlp);
virtual uint8_t packetType() = 0;
@ -99,7 +99,7 @@ struct UDPSocketFace
*/
struct UDPSocketEvents
{
virtual void onDisconnected(UDPSocketFace*) {};
virtual void onDisconnected(UDPSocketFace*) {}
virtual void onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytesConstRef _packetData) = 0;
};
@ -115,7 +115,7 @@ class UDPSocket: UDPSocketFace, public std::enable_shared_from_this<UDPSocket<Ha
{
public:
enum { maxDatagramSize = MaxDatagramSize };
static_assert(maxDatagramSize < 65507, "UDP datagrams cannot be larger than 65507 bytes");
static_assert((unsigned)maxDatagramSize < 65507u, "UDP datagrams cannot be larger than 65507 bytes");
/// Create socket for specific endpoint.
UDPSocket(ba::io_service& _io, UDPSocketEvents& _host, bi::udp::endpoint _endpoint): m_host(_host), m_endpoint(_endpoint), m_socket(_io) { m_started.store(false); m_closed.store(true); };
@ -283,4 +283,4 @@ void UDPSocket<Handler, MaxDatagramSize>::disconnectWithError(boost::system::err
}
}
}
}

2
libsolidity/AST.cpp

@ -890,6 +890,8 @@ void IndexAccess::checkTypeRequirements(TypePointers const*)
ArrayType const& type = dynamic_cast<ArrayType const&>(*m_base->getType());
if (!m_index)
BOOST_THROW_EXCEPTION(createTypeError("Index expression cannot be omitted."));
if (type.isString())
BOOST_THROW_EXCEPTION(createTypeError("Index access for string is not possible."));
m_index->expectType(IntegerType(256));
if (type.isByteArray())
m_type = make_shared<FixedBytesType>(1);

22
libsolidity/ArrayUtils.cpp

@ -364,7 +364,13 @@ void ArrayUtils::clearStorageLoop(Type const& _type) const
return;
}
// stack: end_pos pos
eth::AssemblyItem loopStart = m_context.newTag();
// jump to and return from the loop to allow for duplicate code removal
eth::AssemblyItem returnTag = m_context.pushNewTag();
m_context << eth::Instruction::SWAP2 << eth::Instruction::SWAP1;
// stack: <return tag> end_pos pos
eth::AssemblyItem loopStart = m_context.appendJumpToNew();
m_context << loopStart;
// check for loop condition
m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3
@ -380,7 +386,11 @@ void ArrayUtils::clearStorageLoop(Type const& _type) const
m_context.appendJumpTo(loopStart);
// cleanup
m_context << zeroLoopEnd;
m_context << eth::Instruction::POP;
m_context << eth::Instruction::POP << eth::Instruction::SWAP1;
// "return"
m_context << eth::Instruction::JUMP;
m_context << returnTag;
solAssert(m_context.getStackHeight() == stackHeightStart - 1, "");
}
@ -455,12 +465,10 @@ void ArrayUtils::accessIndex(ArrayType const& _arrayType) const
m_context << eth::Instruction::DUP2 << load;
// stack: <base_ref> <index> <length>
// check out-of-bounds access
m_context << eth::Instruction::DUP2 << eth::Instruction::LT;
eth::AssemblyItem legalAccess = m_context.appendConditionalJump();
// out-of-bounds access throws exception (just STOP for now)
m_context << eth::Instruction::STOP;
m_context << eth::Instruction::DUP2 << eth::Instruction::LT << eth::Instruction::ISZERO;
// out-of-bounds access throws exception
m_context.appendConditionalJumpTo(m_context.errorTag());
m_context << legalAccess;
// stack: <base_ref> <index>
m_context << eth::Instruction::SWAP1;
if (_arrayType.isDynamicallySized())

2
libsolidity/Compiler.cpp

@ -194,7 +194,6 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract)
}
else
m_context << eth::Instruction::STOP; // function not found
for (auto const& it: interfaceFunctions)
{
FunctionTypePointer const& functionType = it.second;
@ -289,7 +288,6 @@ bool Compiler::visit(VariableDeclaration const& _variableDeclaration)
m_breakTags.clear();
m_continueTags.clear();
m_context << m_context.getFunctionEntryLabel(_variableDeclaration);
ExpressionCompiler(m_context, m_optimize).appendStateVariableAccessor(_variableDeclaration);
return false;

2
libsolidity/CompilerContext.h

@ -98,6 +98,8 @@ public:
eth::AssemblyItem appendJumpToNew() { return m_asm.appendJump().tag(); }
/// Appends a JUMP to a tag already on the stack
CompilerContext& appendJump(eth::AssemblyItem::JumpType _jumpType = eth::AssemblyItem::JumpType::Ordinary);
/// Returns an "ErrorTag"
eth::AssemblyItem errorTag() { return m_asm.errorTag(); }
/// Appends a JUMP to a specific tag
CompilerContext& appendJumpTo(eth::AssemblyItem const& _tag) { m_asm.appendJump(_tag); return *this; }
/// Appends pushing of a new tag and @returns the new tag.

10
libsolidity/ExpressionCompiler.cpp

@ -824,7 +824,10 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
if (arrayType.getLocation() == ArrayType::Location::Storage)
{
if (arrayType.isByteArray())
{
solAssert(!arrayType.isString(), "Index access to string is not allowed.");
setLValue<StorageByteArrayElement>(_indexAccess);
}
else
setLValueToStorageItem(_indexAccess);
}
@ -1103,8 +1106,11 @@ void ExpressionCompiler::appendExternalFunctionCall(
m_context << eth::Instruction::CALLCODE;
else
m_context << eth::Instruction::CALL;
auto tag = m_context.appendConditionalJump();
m_context << eth::Instruction::STOP << tag; // STOP if CALL leaves 0.
//Propagate error condition (if CALL pushes 0 on stack).
m_context << eth::Instruction::ISZERO;
m_context.appendConditionalJumpTo(m_context.errorTag());
if (_functionType.valueSet())
m_context << eth::Instruction::POP;
if (_functionType.gasSet())

5
libsolidity/ExpressionCompiler.h

@ -98,10 +98,7 @@ private:
void appendHighBitsCleanup(IntegerType const& _typeOnStack);
/// Appends code to call a function of the given type with the given arguments.
void appendExternalFunctionCall(
FunctionType const& _functionType,
std::vector<ASTPointer<Expression const>> const& _arguments
);
void appendExternalFunctionCall(FunctionType const& _functionType, std::vector<ASTPointer<Expression const>> const& _arguments);
/// Appends code that evaluates the given arguments and moves the result to memory encoded as
/// specified by the ABI. The memory offset is expected to be on the stack and is updated by
/// this call. If @a _padToWordBoundaries is set to false, all values are concatenated without

2
libsolidity/Token.h

@ -286,6 +286,7 @@ namespace solidity
K(Bytes32, "bytes32", 0) \
K(Bytes, "bytes", 0) \
K(Byte, "byte", 0) \
K(String, "string", 0) \
K(Address, "address", 0) \
K(Bool, "bool", 0) \
K(Real, "real", 0) \
@ -312,7 +313,6 @@ namespace solidity
K(Match, "match", 0) \
K(Of, "of", 0) \
K(Relocatable, "relocatable", 0) \
T(String, "string", 0) \
K(Switch, "switch", 0) \
K(Throw, "throw", 0) \
K(Try, "try", 0) \

20
libsolidity/Types.cpp

@ -145,6 +145,8 @@ TypePointer Type::fromElementaryTypeName(Token::Value _typeToken)
return make_shared<BoolType>();
else if (_typeToken == Token::Bytes)
return make_shared<ArrayType>(ArrayType::Location::Storage);
else if (_typeToken == Token::String)
return make_shared<ArrayType>(ArrayType::Location::Storage, true);
else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to convert elementary typename " +
std::string(Token::toString(_typeToken)) + " to type."));
@ -663,7 +665,7 @@ bool ArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const
// let us not allow assignment to memory arrays for now
if (convertTo.getLocation() != Location::Storage)
return false;
if (convertTo.isByteArray() != isByteArray())
if (convertTo.isByteArray() != isByteArray() || convertTo.isString() != isString())
return false;
if (!getBaseType()->isImplicitlyConvertibleTo(*convertTo.getBaseType()))
return false;
@ -684,8 +686,12 @@ bool ArrayType::operator==(Type const& _other) const
if (_other.getCategory() != getCategory())
return false;
ArrayType const& other = dynamic_cast<ArrayType const&>(_other);
if (other.m_location != m_location || other.isByteArray() != isByteArray() ||
other.isDynamicallySized() != isDynamicallySized())
if (
other.m_location != m_location ||
other.isByteArray() != isByteArray() ||
other.isString() != isString() ||
other.isDynamicallySized() != isDynamicallySized()
)
return false;
return isDynamicallySized() || getLength() == other.getLength();
}
@ -736,7 +742,9 @@ unsigned ArrayType::getSizeOnStack() const
string ArrayType::toString() const
{
if (isByteArray())
if (isString())
return "string";
else if (isByteArray())
return "bytes";
string ret = getBaseType()->toString() + "[";
if (!isDynamicallySized())
@ -746,7 +754,7 @@ string ArrayType::toString() const
TypePointer ArrayType::externalType() const
{
if (m_isByteArray)
if (m_arrayKind != ArrayKind::Ordinary)
return shared_from_this();
if (!m_baseType->externalType())
return TypePointer();
@ -762,7 +770,7 @@ TypePointer ArrayType::externalType() const
shared_ptr<ArrayType> ArrayType::copyForLocation(ArrayType::Location _location) const
{
auto copy = make_shared<ArrayType>(_location);
copy->m_isByteArray = m_isByteArray;
copy->m_arrayKind = m_arrayKind;
if (m_baseType->getCategory() == Type::Category::Array)
copy->m_baseType = dynamic_cast<ArrayType const&>(*m_baseType).copyForLocation(_location);
else

22
libsolidity/Types.h

@ -367,10 +367,10 @@ public:
virtual Category getCategory() const override { return Category::Array; }
/// Constructor for a byte array ("bytes")
explicit ArrayType(Location _location):
/// Constructor for a byte array ("bytes") and string.
explicit ArrayType(Location _location, bool _isString = false):
m_location(_location),
m_isByteArray(true),
m_arrayKind(_isString ? ArrayKind::String : ArrayKind::Bytes),
m_baseType(std::make_shared<FixedBytesType>(1))
{}
/// Constructor for a dynamically sized array type ("type[]")
@ -394,11 +394,17 @@ public:
virtual u256 getStorageSize() const override;
virtual unsigned getSizeOnStack() const override;
virtual std::string toString() const override;
virtual MemberList const& getMembers() const override { return s_arrayTypeMemberList; }
virtual MemberList const& getMembers() const override
{
return isString() ? EmptyMemberList : s_arrayTypeMemberList;
}
virtual TypePointer externalType() const override;
Location getLocation() const { return m_location; }
bool isByteArray() const { return m_isByteArray; }
/// @returns true if this is a byte array or a string
bool isByteArray() const { return m_arrayKind != ArrayKind::Ordinary; }
/// @returns true if this is a string
bool isString() const { return m_arrayKind == ArrayKind::String; }
TypePointer const& getBaseType() const { solAssert(!!m_baseType, ""); return m_baseType;}
u256 const& getLength() const { return m_length; }
@ -407,8 +413,12 @@ public:
std::shared_ptr<ArrayType> copyForLocation(Location _location) const;
private:
/// String is interpreted as a subtype of Bytes.
enum class ArrayKind { Ordinary, Bytes, String };
Location m_location;
bool m_isByteArray = false; ///< Byte arrays ("bytes") have different semantics from ordinary arrays.
///< Byte arrays ("bytes") and strings have different semantics from ordinary arrays.
ArrayKind m_arrayKind = ArrayKind::Ordinary;
TypePointer m_baseType;
bool m_hasDynamicLength = true;
u256 m_length;

12
libweb3jsonrpc/WebThreeStubServerBase.cpp

@ -267,7 +267,7 @@ static shh::Envelope toSealed(Json::Value const& _json, shh::Message const& _m,
return _m.seal(_from, bt, ttl, workToProve);
}
static pair<shh::FullTopic, Public> toWatch(Json::Value const& _json)
static pair<shh::Topics, Public> toWatch(Json::Value const& _json)
{
shh::BuildTopic bt;
Public to;
@ -985,7 +985,7 @@ string WebThreeStubServerBase::shh_newFilter(Json::Value const& _json)
try
{
pair<shh::FullTopic, Public> w = toWatch(_json);
pair<shh::Topics, Public> w = toWatch(_json);
auto ret = face()->installWatch(w.first);
m_shhWatches.insert(make_pair(ret, w.second));
return toJS(ret);
@ -1025,10 +1025,10 @@ Json::Value WebThreeStubServerBase::shh_getFilterChanges(string const& _filterId
if (pub)
{
cwarn << "Silently decrypting message from identity" << pub << ": User validation hook goes here.";
m = e.open(face()->fullTopic(id), m_shhIds[pub]);
m = e.open(face()->fullTopics(id), m_shhIds[pub]);
}
else
m = e.open(face()->fullTopic(id));
m = e.open(face()->fullTopics(id));
if (!m)
continue;
ret.append(toJson(h, e, m));
@ -1058,10 +1058,10 @@ Json::Value WebThreeStubServerBase::shh_getMessages(string const& _filterId)
if (pub)
{
cwarn << "Silently decrypting message from identity" << pub << ": User validation hook goes here.";
m = e.open(face()->fullTopic(id), m_shhIds[pub]);
m = e.open(face()->fullTopics(id), m_shhIds[pub]);
}
else
m = e.open(face()->fullTopic(id));
m = e.open(face()->fullTopics(id));
if (!m)
continue;
ret.append(toJson(h, e, m));

24
libwhisper/Common.cpp

@ -28,26 +28,26 @@ using namespace dev;
using namespace dev::p2p;
using namespace dev::shh;
CollapsedTopicPart dev::shh::collapse(FullTopicPart const& _p)
AbridgedTopic dev::shh::abridge(Topic const& _p)
{
return CollapsedTopicPart(sha3(_p));
return AbridgedTopic(sha3(_p));
}
CollapsedTopic dev::shh::collapse(FullTopic const& _fullTopic)
AbridgedTopics dev::shh::abridge(Topics const& _topics)
{
CollapsedTopic ret;
ret.reserve(_fullTopic.size());
for (auto const& ft: _fullTopic)
ret.push_back(collapse(ft));
AbridgedTopics ret;
ret.reserve(_topics.size());
for (auto const& t : _topics)
ret.push_back(abridge(t));
return ret;
}
CollapsedTopic BuildTopic::toTopic() const
AbridgedTopics BuildTopic::toAbridgedTopics() const
{
CollapsedTopic ret;
AbridgedTopics ret;
ret.reserve(m_parts.size());
for (auto const& h: m_parts)
ret.push_back(collapse(h));
ret.push_back(abridge(h));
return ret;
}
@ -71,7 +71,7 @@ bool TopicFilter::matches(Envelope const& _e) const
for (unsigned i = 0; i < t.size(); ++i)
{
for (auto et: _e.topic())
if (((t[i].first ^ et) & t[i].second) == CollapsedTopicPart())
if (((t[i].first ^ et) & t[i].second) == AbridgedTopic())
goto NEXT_TOPICPART;
// failed to match topicmask against any topics: move on to next mask
goto NEXT_TOPICMASK;
@ -89,7 +89,7 @@ TopicMask BuildTopicMask::toTopicMask() const
TopicMask ret;
ret.reserve(m_parts.size());
for (auto const& h: m_parts)
ret.push_back(make_pair(collapse(h), ~CollapsedTopicPart()));
ret.push_back(make_pair(abridge(h), ~AbridgedTopic()));
return ret;
}

28
libwhisper/Common.h

@ -60,14 +60,14 @@ enum WhisperPacket
PacketCount
};
using CollapsedTopicPart = FixedHash<4>;
using FullTopicPart = h256;
using AbridgedTopic = FixedHash<4>;
using Topic = h256;
using CollapsedTopic = std::vector<CollapsedTopicPart>;
using FullTopic = h256s;
using AbridgedTopics = std::vector<AbridgedTopic>;
using Topics = h256s;
CollapsedTopicPart collapse(FullTopicPart const& _fullTopicPart);
CollapsedTopic collapse(FullTopic const& _fullTopic);
AbridgedTopic abridge(Topic const& _topic);
AbridgedTopics abridge(Topics const& _topics);
class BuildTopic
{
@ -80,10 +80,10 @@ public:
BuildTopic& shiftRaw(h256 const& _part) { m_parts.push_back(_part); return *this; }
operator CollapsedTopic() const { return toTopic(); }
operator FullTopic() const { return toFullTopic(); }
CollapsedTopic toTopic() const;
FullTopic toFullTopic() const { return m_parts; }
operator AbridgedTopics() const { return toAbridgedTopics(); }
operator Topics() const { return toTopics(); }
AbridgedTopics toAbridgedTopics() const;
Topics toTopics() const { return m_parts; }
protected:
BuildTopic& shiftBytes(bytes const& _b);
@ -91,14 +91,14 @@ protected:
h256s m_parts;
};
using TopicMask = std::vector<std::pair<CollapsedTopicPart, CollapsedTopicPart>>;
using TopicMask = std::vector<std::pair<AbridgedTopic, AbridgedTopic>>;
using TopicMasks = std::vector<TopicMask>;
class TopicFilter
{
public:
TopicFilter() {}
TopicFilter(FullTopic const& _m) { m_topicMasks.push_back(TopicMask()); for (auto const& h: _m) m_topicMasks.back().push_back(std::make_pair(collapse(h), h ? ~CollapsedTopicPart() : CollapsedTopicPart())); }
TopicFilter(Topics const& _m) { m_topicMasks.push_back(TopicMask()); for (auto const& h: _m) m_topicMasks.back().push_back(std::make_pair(abridge(h), h ? ~AbridgedTopic() : AbridgedTopic())); }
TopicFilter(TopicMask const& _m): m_topicMasks(1, _m) {}
TopicFilter(TopicMasks const& _m): m_topicMasks(_m) {}
TopicFilter(RLP const& _r)//: m_topicMasks(_r.toVector<std::vector<>>())
@ -132,9 +132,9 @@ public:
template <class T> BuildTopicMask& operator()(T const& _t) { shift(_t); return *this; }
operator TopicMask() const { return toTopicMask(); }
operator FullTopic() const { return toFullTopic(); }
operator Topics() const { return toTopics(); }
TopicMask toTopicMask() const;
FullTopic toFullTopic() const { return m_parts; }
Topics toTopics() const { return m_parts; }
};
}

24
libwhisper/Interface.h

@ -38,19 +38,13 @@ namespace dev
namespace shh
{
/*struct TopicMask
{
Topic data;
Topic mask;
};*/
class Watch;
struct InstalledFilter
{
InstalledFilter(FullTopic const& _f): full(_f), filter(_f) {}
InstalledFilter(Topics const& _t): full(_t), filter(_t) {}
FullTopic full;
Topics full;
TopicFilter filter;
unsigned refCount = 1;
};
@ -71,8 +65,8 @@ public:
virtual void inject(Envelope const& _m, WhisperPeer* _from = nullptr) = 0;
virtual FullTopic const& fullTopic(unsigned _id) const = 0;
virtual unsigned installWatch(FullTopic const& _mask) = 0;
virtual Topics const& fullTopics(unsigned _id) const = 0;
virtual unsigned installWatch(Topics const& _filter) = 0;
virtual unsigned installWatchOnId(h256 _filterId) = 0;
virtual void uninstallWatch(unsigned _watchId) = 0;
virtual h256s peekWatch(unsigned _watchId) const = 0;
@ -81,10 +75,10 @@ public:
virtual Envelope envelope(h256 _m) const = 0;
void post(bytes const& _payload, FullTopic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).seal(_topic, _ttl, _workToProve)); }
void post(Public _to, bytes const& _payload, FullTopic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).sealTo(_to, _topic, _ttl, _workToProve)); }
void post(Secret _from, bytes const& _payload, FullTopic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).seal(_from, _topic, _ttl, _workToProve)); }
void post(Secret _from, Public _to, bytes const& _payload, FullTopic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).sealTo(_from, _to, _topic, _ttl, _workToProve)); }
void post(bytes const& _payload, Topics _topics, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).seal(_topics, _ttl, _workToProve)); }
void post(Public _to, bytes const& _payload, Topics _topics, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).sealTo(_to, _topics, _ttl, _workToProve)); }
void post(Secret _from, bytes const& _payload, Topics _topics, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).seal(_from, _topics, _ttl, _workToProve)); }
void post(Secret _from, Public _to, bytes const& _payload, Topics _topics, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).sealTo(_from, _to, _topics, _ttl, _workToProve)); }
};
struct WatshhChannel: public dev::LogChannel { static const char* name() { return "shh"; } static const int verbosity = 1; };
@ -106,7 +100,7 @@ class Watch: public boost::noncopyable
public:
Watch() {}
Watch(Interface& _c, FullTopic const& _f): m_c(&_c), m_id(_c.installWatch(_f)) {}
Watch(Interface& _c, Topics const& _t): m_c(&_c), m_id(_c.installWatch(_t)) {}
~Watch() { if (m_c) m_c->uninstallWatch(m_id); }
h256s check() { return m_c ? m_c->checkWatch(m_id) : h256s(); }

20
libwhisper/Message.cpp

@ -26,7 +26,7 @@ using namespace dev;
using namespace dev::p2p;
using namespace dev::shh;
Message::Message(Envelope const& _e, FullTopic const& _fk, Secret const& _s)
Message::Message(Envelope const& _e, Topics const& _t, Secret const& _s)
{
try
{
@ -35,7 +35,7 @@ Message::Message(Envelope const& _e, FullTopic const& _fk, Secret const& _s)
if (!decrypt(_s, &(_e.data()), b))
return;
else{}
else if (!openBroadcastEnvelope(_e, _fk, b))
else if (!openBroadcastEnvelope(_e, _t, b))
return;
if (populate(b))
@ -47,14 +47,14 @@ Message::Message(Envelope const& _e, FullTopic const& _fk, Secret const& _s)
}
}
bool Message::openBroadcastEnvelope(Envelope const& _e, FullTopic const& _fk, bytes& o_b)
bool Message::openBroadcastEnvelope(Envelope const& _e, Topics const& _fk, bytes& o_b)
{
// retrieve the key using the known topic and topicIndex.
unsigned topicIndex = 0;
Secret topicSecret;
// determine topicSecret/topicIndex from knowledge of the collapsed topics (which give the order) and our full-size filter topic.
CollapsedTopic knownTopic = collapse(_fk);
AbridgedTopics knownTopic = abridge(_fk);
for (unsigned ti = 0; ti < _fk.size() && !topicSecret; ++ti)
for (unsigned i = 0; i < _e.topic().size(); ++i)
if (_e.topic()[i] == knownTopic[ti])
@ -96,10 +96,10 @@ bool Message::populate(bytes const& _data)
return true;
}
Envelope Message::seal(Secret _from, FullTopic const& _fullTopic, unsigned _ttl, unsigned _workToProve) const
Envelope Message::seal(Secret _from, Topics const& _fullTopics, unsigned _ttl, unsigned _workToProve) const
{
CollapsedTopic topic = collapse(_fullTopic);
Envelope ret(time(0) + _ttl, _ttl, topic);
AbridgedTopics topics = abridge(_fullTopics);
Envelope ret(time(0) + _ttl, _ttl, topics);
bytes input(1 + m_payload.size());
input[0] = 0;
@ -121,7 +121,7 @@ Envelope Message::seal(Secret _from, FullTopic const& _fullTopic, unsigned _ttl,
// this message is for broadcast (could be read by anyone who knows at least one of the topics)
// create the shared secret for encrypting the payload, then encrypt the shared secret with each topic
Secret s = Secret::random();
for (h256 const& t : _fullTopic)
for (h256 const& t : _fullTopics)
{
h256 salt = h256::random();
ret.m_data += (generateGamma(t, salt) ^ s).asBytes();
@ -146,9 +146,9 @@ Envelope::Envelope(RLP const& _m)
m_nonce = _m[4].toInt<u256>();
}
Message Envelope::open(FullTopic const& _ft, Secret const& _s) const
Message Envelope::open(Topics const& _t, Secret const& _s) const
{
return Message(*this, _ft, _s);
return Message(*this, _t, _s);
}
unsigned Envelope::workProved() const

20
libwhisper/Message.h

@ -72,22 +72,22 @@ public:
unsigned sent() const { return m_expiry - m_ttl; }
unsigned expiry() const { return m_expiry; }
unsigned ttl() const { return m_ttl; }
CollapsedTopic const& topic() const { return m_topic; }
AbridgedTopics const& topic() const { return m_topic; }
bytes const& data() const { return m_data; }
Message open(FullTopic const& _ft, Secret const& _s = Secret()) const;
Message open(Topics const& _t, Secret const& _s = Secret()) const;
unsigned workProved() const;
void proveWork(unsigned _ms);
private:
Envelope(unsigned _exp, unsigned _ttl, CollapsedTopic const& _topic): m_expiry(_exp), m_ttl(_ttl), m_topic(_topic) {}
Envelope(unsigned _exp, unsigned _ttl, AbridgedTopics const& _topic): m_expiry(_exp), m_ttl(_ttl), m_topic(_topic) {}
unsigned m_expiry = 0;
unsigned m_ttl = 0;
u256 m_nonce;
CollapsedTopic m_topic;
AbridgedTopics m_topic;
bytes m_data;
};
@ -102,7 +102,7 @@ class Message
{
public:
Message() {}
Message(Envelope const& _e, FullTopic const& _ft, Secret const& _s = Secret());
Message(Envelope const& _e, Topics const& _t, Secret const& _s = Secret());
Message(bytes const& _payload): m_payload(_payload) {}
Message(bytesConstRef _payload): m_payload(_payload.toBytes()) {}
Message(bytes&& _payload) { std::swap(_payload, m_payload); }
@ -119,15 +119,15 @@ public:
operator bool() const { return !!m_payload.size() || m_from || m_to; }
/// Turn this message into a ditributable Envelope.
Envelope seal(Secret _from, FullTopic const& _topic, unsigned _ttl = 50, unsigned _workToProve = 50) const;
Envelope seal(Secret _from, Topics const& _topics, unsigned _ttl = 50, unsigned _workToProve = 50) const;
// Overloads for skipping _from or specifying _to.
Envelope seal(FullTopic const& _topic, unsigned _ttl = 50, unsigned _workToProve = 50) const { return seal(Secret(), _topic, _ttl, _workToProve); }
Envelope sealTo(Public _to, FullTopic const& _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { m_to = _to; return seal(Secret(), _topic, _ttl, _workToProve); }
Envelope sealTo(Secret _from, Public _to, FullTopic const& _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { m_to = _to; return seal(_from, _topic, _ttl, _workToProve); }
Envelope seal(Topics const& _topics, unsigned _ttl = 50, unsigned _workToProve = 50) const { return seal(Secret(), _topics, _ttl, _workToProve); }
Envelope sealTo(Public _to, Topics const& _topics, unsigned _ttl = 50, unsigned _workToProve = 50) { m_to = _to; return seal(Secret(), _topics, _ttl, _workToProve); }
Envelope sealTo(Secret _from, Public _to, Topics const& _topics, unsigned _ttl = 50, unsigned _workToProve = 50) { m_to = _to; return seal(_from, _topics, _ttl, _workToProve); }
private:
bool populate(bytes const& _data);
bool openBroadcastEnvelope(Envelope const& _e, FullTopic const& _fk, bytes& o_b);
bool openBroadcastEnvelope(Envelope const& _e, Topics const& _t, bytes& o_b);
h256 generateGamma(h256 const& _key, h256 const& _salt) const { return sha3(_key ^ _salt); }
Public m_from;

4
libwhisper/WhisperHost.cpp

@ -103,11 +103,11 @@ unsigned WhisperHost::installWatchOnId(h256 _h)
return ret;
}
unsigned WhisperHost::installWatch(shh::FullTopic const& _ft)
unsigned WhisperHost::installWatch(shh::Topics const& _t)
{
Guard l(m_filterLock);
InstalledFilter f(_ft);
InstalledFilter f(_t);
h256 h = f.filter.sha3();
if (!m_filters.count(h))

6
libwhisper/WhisperHost.h

@ -40,7 +40,7 @@ namespace dev
namespace shh
{
static const FullTopic EmptyFullTopic;
static const Topics EmptyTopics;
class WhisperHost: public HostCapability<WhisperPeer>, public Interface, public Worker
{
@ -54,8 +54,8 @@ public:
virtual void inject(Envelope const& _e, WhisperPeer* _from = nullptr) override;
virtual FullTopic const& fullTopic(unsigned _id) const { try { return m_filters.at(m_watches.at(_id).id).full; } catch (...) { return EmptyFullTopic; } }
virtual unsigned installWatch(FullTopic const& _filter) override;
virtual Topics const& fullTopics(unsigned _id) const { try { return m_filters.at(m_watches.at(_id).id).full; } catch (...) { return EmptyTopics; } }
virtual unsigned installWatch(Topics const& _filter) override;
virtual unsigned installWatchOnId(h256 _filterId) override;
virtual void uninstallWatch(unsigned _watchId) override;
virtual h256s peekWatch(unsigned _watchId) const override { dev::Guard l(m_filterLock); try { return m_watches.at(_watchId).changes; } catch (...) { return h256s(); } }

4
mix/CodeModel.cpp

@ -523,7 +523,9 @@ SolidityType CodeModel::nodeType(dev::solidity::Type const* _type)
case Type::Category::Array:
{
ArrayType const* array = dynamic_cast<ArrayType const*>(_type);
if (array->isByteArray())
if (array->isString())
r.type = SolidityType::Type::String;
else if (array->isByteArray())
r.type = SolidityType::Type::Bytes;
else
{

18
mix/MixClient.cpp

@ -143,7 +143,7 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c
bytesConstRef const* lastData = nullptr;
unsigned codeIndex = 0;
unsigned dataIndex = 0;
auto onOp = [&](uint64_t steps, Instruction inst, dev::bigint newMemSize, dev::bigint gasCost, void* voidVM, void const* voidExt)
auto onOp = [&](uint64_t steps, Instruction inst, bigint newMemSize, bigint gasCost, bigint gas, void* voidVM, void const* voidExt)
{
VM& vm = *static_cast<VM*>(voidVM);
ExtVM const& ext = *static_cast<ExtVM const*>(voidExt);
@ -180,8 +180,20 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c
else
levels.resize(ext.depth);
machineStates.emplace_back(MachineState({steps, vm.curPC(), inst, newMemSize, vm.gas(),
vm.stack(), vm.memory(), gasCost, ext.state().storage(ext.myAddress), levels, codeIndex, dataIndex}));
machineStates.push_back(MachineState{
steps,
vm.curPC(),
inst,
newMemSize,
static_cast<u256>(gas),
vm.stack(),
vm.memory(),
gasCost,
ext.state().storage(ext.myAddress),
std::move(levels),
codeIndex,
dataIndex
});
};
execution.go(onOp);

37
mix/QBigInt.cpp

@ -57,3 +57,40 @@ QBigInt* QBigInt::divide(QBigInt* const& _value) const
BigIntVariant toDivide = _value->internalValue();
return new QBigInt(boost::apply_visitor(mix::divide(), m_internalValue, toDivide));
}
QVariantMap QBigInt::checkAgainst(QString const& _type) const
{
QVariantMap ret;
QString type = _type;
QString capacity = type.replace("uint", "").replace("int", "");
if (capacity.isEmpty())
capacity = "256";
bigint range = 1;
for (int k = 0; k < capacity.toInt() / 8; ++k)
range = range * 256;
bigint value = boost::get<bigint>(this->internalValue());
ret.insert("valid", true);
if (_type.startsWith("uint") && value > range - 1)
{
ret.insert("minValue", "0");
std::ostringstream s;
s << range - 1;
ret.insert("maxValue", QString::fromStdString(s.str()));
if (value > range)
ret["valid"] = false;
}
else if (_type.startsWith("int"))
{
range = range / 2;
std::ostringstream s;
s << -range;
ret.insert("minValue", QString::fromStdString(s.str()));
s.str("");
s.clear();
s << range - 1;
ret.insert("maxValue", QString::fromStdString(s.str()));
if (-range > value || value > range - 1)
ret["valid"] = false;
}
return ret;
}

3
mix/QBigInt.h

@ -84,6 +84,7 @@ public:
Q_INVOKABLE QString value() const;
/// Set the value of the BigInteger used. Will use u256 type. Invokable from QML.
Q_INVOKABLE void setValue(QString const& _value) { m_internalValue = dev::jsToU256(_value.toStdString()); }
Q_INVOKABLE void setBigInt(QString const& _value) { m_internalValue = bigint(_value.toStdString()); }
/// Subtract by @a _value. Invokable from QML.
Q_INVOKABLE QBigInt* subtract(QBigInt* const& _value) const;
/// Add @a _value to the current big integer. Invokable from QML.
@ -92,6 +93,8 @@ public:
Q_INVOKABLE QBigInt* multiply(QBigInt* const& _value) const;
/// divide by @a _value. Invokable from QML.
Q_INVOKABLE QBigInt* divide(QBigInt* const& _value) const;
/// check if the current value satisfy the given type
Q_INVOKABLE QVariantMap checkAgainst(QString const& _type) const;
protected:
BigIntVariant m_internalValue;

1
mix/SolidityType.h

@ -45,6 +45,7 @@ struct SolidityType
Bool,
Address,
Bytes,
String,
Enum,
Struct
};

1
mix/qml.qrc

@ -63,5 +63,6 @@
<file>qml/js/Printer.js</file>
<file>qml/js/ansi2html.js</file>
<file>qml/js/NetworkDeployment.js</file>
<file>qml/js/InputValidator.js</file>
</qresource>
</RCC>

7
mix/qml/QIntTypeView.qml

@ -21,8 +21,15 @@ Item
clip: true
selectByMouse: true
text: value
anchors.fill: parent
font.pointSize: dbgStyle.general.basicFontSize
color: dbgStyle.general.basicColor
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
onClicked: textinput.forceActiveFocus()
}
}
}
}

23
mix/qml/TransactionDialog.qml

@ -6,6 +6,7 @@ import QtQuick.Window 2.0
import QtQuick.Controls.Styles 1.3
import org.ethereum.qml.QEther 1.0
import "js/TransactionHelper.js" as TransactionHelper
import "js/InputValidator.js" as InputValidator
import "."
Dialog {
@ -503,10 +504,22 @@ Dialog {
anchors.right: parent.right;
Button {
text: qsTr("OK");
onClicked: {
close();
accepted();
var invalid = InputValidator.validate(paramsModel, paramValues);
if (invalid.length === 0)
{
close();
accepted();
}
else
{
errorDialog.text = qsTr("Some parameters are invalid:\n");
for (var k in invalid)
errorDialog.text += invalid[k].message + "\n";
errorDialog.open();
}
}
}
@ -514,6 +527,12 @@ Dialog {
text: qsTr("Cancel");
onClicked: close();
}
MessageDialog {
id: errorDialog
standardButtons: StandardButton.Ok
icon: StandardIcon.Critical
}
}
}
}

125
mix/qml/js/InputValidator.js

@ -0,0 +1,125 @@
Qt.include("QEtherHelper.js")
var nbRegEx = new RegExp('^[0-9]+$');
function validate(model, values)
{
var inError = [];
for (var k in model)
{
if (values[model[k].name])
{
var type = model[k].type.name;
var res;
if (isContractType(type))
res = validateAddress(type, values[model[k].name]);
else if (type.indexOf("int") !== -1)
res = validateInt(type, values[model[k].name]);
else if (type.indexOf("bytes") !== -1)
res = validateBytes(type, values[model[k].name]);
else if (type.indexOf("bool") !== -1)
res = validateBool(type, values[model[k].name]);
else if (type.indexOf("address") !== -1)
res = validateAddress(type, values[model[k].name]);
else
res.valid = true;
if (!res.valid)
inError.push({ type: type, value: values, message: res.message });
}
}
return inError;
}
function isContractType(_type)
{
for (var k in Object.keys(codeModel.contracts))
{
if ("contract " + Object.keys(codeModel.contracts)[k] === _type)
return true;
}
return false;
}
function validateInt(_type, _value)
{
var ret = { valid: true, message: "" }
if (_value.indexOf("-") === 0)
{
_value = _value.substring(1);
if (_type.indexOf("uint") === -1)
{
ret.valid = false;
ret.message = "uint type cannot represent negative number";
}
}
ret.valid = nbRegEx.test(_value);
if (!ret.valid)
ret.message = _value + " does not represent " + _type + " type.";
else
{
var bigInt = createBigInt(_value);
bigInt.setBigInt(_value);
var result = bigInt.checkAgainst(_type);
if (!result.valid)
{
ret.valid = false;
ret.message = _type + " should be between " + result.minValue + " and " + result.maxValue;
}
}
return ret;
}
function validateAddress(_type, _value)
{
var ret = { valid: true, message: "" }
if (_value.indexOf("<") === 0 && _value.indexOf(">") === _value.length - 1)
{
var v = _value.split(' - ');
if (v.length !== 2 || !nbRegEx.test(v[1].replace(">", ""))) // <Contract - 2>
{
ret.valid = false;
ret.message = _value + " is not a valid token for address type.";
}
}
else if (_value.indexOf("0x") !== 0)
{
ret.valid = false
ret.message = "Address type should start with 0x.";
}
else
{
_value = _value.substring(2);
if (_value.length !== 40)
{
ret.valid = false
ret.message = "Address type should contain 40 characters.";
}
}
return ret;
}
function validateBytes(_type, _value)
{
var ret = { valid: true, message: "" }
if (_value.length > parseInt(_type.replace("bytes", "")) )
{
ret.valid = false;
ret.message = _type + " should not contains more than " + _type.replace("bytes", "") + " characters";
}
return ret;
}
function validateBool(_type, _value)
{
var ret = { valid: true, message: "" }
if (_value !== "1" && _value !== "0")
{
ret.valid = false;
ret.message = _value + " is not in the correct bool format";
}
return ret;
}
function validateEnum(_type, _value)
{
}

57
solc/jsonCompiler.cpp

@ -27,6 +27,7 @@
#include <libdevcore/CommonData.h>
#include <libdevcore/CommonIO.h>
#include <libevmcore/Instruction.h>
#include <libevmcore/Params.h>
#include <libsolidity/Scanner.h>
#include <libsolidity/Parser.h>
#include <libsolidity/ASTPrinter.h>
@ -58,6 +59,61 @@ Json::Value functionHashes(ContractDefinition const& _contract)
return functionHashes;
}
Json::Value gasToJson(GasEstimator::GasConsumption const& _gas)
{
if (_gas.isInfinite || _gas.value > std::numeric_limits<Json::LargestUInt>::max())
return Json::Value(Json::nullValue);
else
return Json::Value(Json::LargestUInt(_gas.value));
}
Json::Value estimateGas(CompilerStack const& _compiler, string const& _contract)
{
Json::Value gasEstimates(Json::objectValue);
using Gas = GasEstimator::GasConsumption;
if (!_compiler.getAssemblyItems(_contract) && !_compiler.getRuntimeAssemblyItems(_contract))
return gasEstimates;
if (eth::AssemblyItems const* items = _compiler.getAssemblyItems(_contract))
{
Gas gas = GasEstimator::functionalEstimation(*items);
u256 bytecodeSize(_compiler.getRuntimeBytecode(_contract).size());
Json::Value creationGas(Json::arrayValue);
creationGas[0] = gasToJson(gas);
creationGas[1] = gasToJson(bytecodeSize * eth::c_createDataGas);
gasEstimates["creation"] = creationGas;
}
if (eth::AssemblyItems const* items = _compiler.getRuntimeAssemblyItems(_contract))
{
ContractDefinition const& contract = _compiler.getContractDefinition(_contract);
Json::Value externalFunctions(Json::objectValue);
for (auto it: contract.getInterfaceFunctions())
{
string sig = it.second->externalSignature();
externalFunctions[sig] = gasToJson(GasEstimator::functionalEstimation(*items, sig));
}
gasEstimates["external"] = externalFunctions;
Json::Value internalFunctions(Json::objectValue);
for (auto const& it: contract.getDefinedFunctions())
{
if (it->isPartOfExternalInterface() || it->isConstructor())
continue;
size_t entry = _compiler.getFunctionEntryPoint(_contract, *it);
GasEstimator::GasConsumption gas = GasEstimator::GasConsumption::infinite();
if (entry > 0)
gas = GasEstimator::functionalEstimation(*items, entry, *it);
FunctionType type(*it);
string sig = it->getName() + "(";
auto end = type.getParameterTypes().end();
for (auto it = type.getParameterTypes().begin(); it != end; ++it)
sig += (*it)->toString() + (it + 1 == end ? "" : ",");
sig += ")";
internalFunctions[sig] = gasToJson(gas);
}
gasEstimates["internal"] = internalFunctions;
}
return gasEstimates;
}
string compile(string _input, bool _optimize)
{
StringMap sources;
@ -109,6 +165,7 @@ string compile(string _input, bool _optimize)
contractData["bytecode"] = toHex(compiler.getBytecode(contractName));
contractData["opcodes"] = eth::disassemble(compiler.getBytecode(contractName));
contractData["functionHashes"] = functionHashes(compiler.getContractDefinition(contractName));
contractData["gasEstimates"] = estimateGas(compiler, contractName);
ostringstream unused;
contractData["assembly"] = compiler.streamAssembly(unused, contractName, sources, true);
output["contracts"][contractName] = contractData;

1
test/CMakeLists.txt

@ -25,6 +25,7 @@ add_subdirectory(libethereum)
add_subdirectory(libevm)
add_subdirectory(libnatspec)
add_subdirectory(libp2p)
add_subdirectory(external-dependencies)
if (JSCONSOLE)
add_subdirectory(libjsengine)

7
test/TestHelper.cpp

@ -328,7 +328,7 @@ void ImportTest::exportTest(bytes const& _output, State const& _statePost)
{
// export output
m_TestObject["out"] = _output.size() > 4096 ? "#" + toString(_output.size()) : toHex(_output, 2, HexPrefix::Add);
m_TestObject["out"] = (_output.size() > 4096 && !Options::get().fulloutput) ? "#" + toString(_output.size()) : toHex(_output, 2, HexPrefix::Add);
// export logs
m_TestObject["logs"] = exportLog(_statePost.pending().size() ? _statePost.log(0) : LogEntries());
@ -588,7 +588,7 @@ void userDefinedTest(std::function<void(json_spirit::mValue&, bool)> doTests)
oSingleTest[pos->first] = pos->second;
json_spirit::mValue v_singleTest(oSingleTest);
doTests(v_singleTest, false);
doTests(v_singleTest, test::Options::get().fillTests);
}
catch (Exception const& _e)
{
@ -760,6 +760,8 @@ Options::Options()
else
singleTestName = std::move(name1);
}
else if (arg == "--fulloutput")
fulloutput = true;
}
}
@ -769,7 +771,6 @@ Options const& Options::get()
return instance;
}
LastHashes lastHashes(u256 _currentBlockNumber)
{
LastHashes ret;

1
test/TestHelper.h

@ -184,6 +184,7 @@ public:
bool stats = false; ///< Execution time stats
std::string statsOutFile; ///< Stats output file. "out" for standard output
bool checkState = false;///< Throw error when checking test states
bool fulloutput = false;///< Replace large output to just it's length
/// Test selection
/// @{

5
test/external-dependencies/CMakeLists.txt

@ -0,0 +1,5 @@
cmake_policy(SET CMP0015 NEW)
aux_source_directory(. SRCS)
add_sources(${SRCS})

37
test/external-dependencies/boost.cpp

@ -0,0 +1,37 @@
/*
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 boost.cpp
* @author Lefteris Karapetsas <lefteris@ethdev.com>
* @date 2015
* Tests for external dependencies: Boost
*/
#include <boost/test/unit_test.hpp>
#include <libdevcore/Common.h>
BOOST_AUTO_TEST_SUITE(ExtDepBoost)
// test that reproduces issue https://github.com/ethereum/cpp-ethereum/issues/1977
BOOST_AUTO_TEST_CASE(u256_overflow_test)
{
dev::u256 a = 14;
dev::bigint b = dev::bigint("115792089237316195423570985008687907853269984665640564039457584007913129639948");
// to fix cast `a` to dev::bigint
BOOST_CHECK(a < b);
}
BOOST_AUTO_TEST_SUITE_END()

7
test/fuzzTesting/CMakeLists.txt

@ -9,10 +9,13 @@ include_directories(${CRYPTOPP_INCLUDE_DIRS})
include_directories(${JSON_RPC_CPP_INCLUDE_DIRS})
add_executable(createRandomVMTest "./createRandomVMTest.cpp" "../libevm/vm.cpp" "../TestHelper.cpp" "../Stats.cpp")
add_executable(createRandomStateTest "./createRandomStateTest.cpp" "../TestHelper.cpp" "../Stats.cpp")
add_executable(checkRandomVMTest "./checkRandomVMTest.cpp" "../libevm/vm.cpp" "../TestHelper.cpp" "../Stats.cpp")
add_executable(createRandomStateTest "./createRandomStateTest.cpp" "../TestHelper.cpp" "../Stats.cpp" "fuzzHelper.cpp")
add_executable(checkRandomVMTest "./checkRandomVMTest.cpp" "../libevm/vm.cpp" "../TestHelper.cpp" "../Stats.cpp" )
add_executable(checkRandomStateTest "./checkRandomStateTest.cpp" "../TestHelper.cpp" "../Stats.cpp")
list(APPEND SRCS "./fuzzHelper.cpp")
add_sources(${SRCS})
target_link_libraries(createRandomVMTest ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES})
target_link_libraries(createRandomVMTest ethereum)
target_link_libraries(createRandomVMTest ethcore)

8
test/fuzzTesting/checkRandomVMTest.cpp

@ -94,13 +94,11 @@ bool doVMTest(mValue& _v)
}
bytes output;
u256 gas;
bool vmExceptionOccured = false;
try
{
auto vm = eth::VMFactory::create(fev.gas);
output = vm->go(fev, fev.simpleTrace()).toBytes();
gas = vm->gas();
auto vm = eth::VMFactory::create();
output = vm->go(fev.gas, fev, fev.simpleTrace()).toBytes();
}
catch (eth::VMException)
{
@ -168,7 +166,7 @@ bool doVMTest(mValue& _v)
return 1;
}
if (asserts(toInt(o["gas"]) == gas))
if (asserts(toInt(o["gas"]) == fev.gas))
return 1;
auto& expectedAddrs = test.addresses;

204
test/fuzzTesting/createRandomStateTest.cpp

@ -37,24 +37,88 @@
#include <libevm/VMFactory.h>
#include <test/libevm/vm.h>
#include <test/TestHelper.h>
#include <test/fuzzTesting/fuzzHelper.h>
using namespace std;
using namespace json_spirit;
using namespace dev;
void doStateTests(json_spirit::mValue& _v);
void doChristophAlgo();
void doRandomCodeAlgo();
string const c_testExample = R"(
{
"randomStatetest" : {
"env" : {
"currentCoinbase" : "945304eb96065b2a98b57a48a06ae28d285a71b5",
"currentDifficulty" : "5623894562375",
"currentGasLimit" : "115792089237316195423570985008687907853269984665640564039457584007913129639935",
"currentNumber" : "0",
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "0",
"code" : "0x6001600101600055",
"nonce" : "0",
"storage" : {
}
},
"945304eb96065b2a98b57a48a06ae28d285a71b5" : {
"balance" : "46",
"code" : "0x6000355415600957005b60203560003555",
"nonce" : "0",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "1000000000000000000",
"code" : "0x",
"nonce" : "0",
"storage" : {
}
}
},
"transaction" : {
"data" : "0x42",
"gasLimit" : "400000",
"gasPrice" : "1",
"nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "100000"
}
}
}
)";
int main(int argc, char *argv[])
{
for (auto i = 0; i < argc; ++i)
{
auto arg = std::string{argv[i]};
dev::test::Options& options = const_cast<dev::test::Options&>(dev::test::Options::get());
if (arg == "--fulloutput")
options.fulloutput = true;
}
//doChristophAlgo();
doRandomCodeAlgo();
return 0;
}
void doChristophAlgo()
{
g_logVerbosity = 0;
// create random code
boost::random::mt19937 gen;
auto now = chrono::steady_clock::now().time_since_epoch();
auto timeSinceEpoch = chrono::duration_cast<chrono::nanoseconds>(now).count();
gen.seed(static_cast<unsigned int>(timeSinceEpoch));
// set min and max length of the random evm code
boost::random::uniform_int_distribution<> lengthOfCodeDist(8, 24);
boost::random::uniform_int_distribution<> reasonableInputValuesSize(0, 7);
@ -77,18 +141,16 @@ int main(int argc, char *argv[])
reasonableInputValues.push_back(u256("0x945304eb96065b2a98b57a48a06ae28d285a71b5"));
reasonableInputValues.push_back(randGenUniformInt());
int lengthOfCode = lengthOfCodeDist(gen);
int lengthOfCode = lengthOfCodeDist(gen);
string randomCode;
for (int i = 0; i < lengthOfCode; ++i)
{
// pre-fill stack to avoid that most of the test fail with a stackunderflow
if (i < 8 && (randGen() < 192))
{
randomCode += randGen() < 32 ? toHex(toCompactBigEndian((uint8_t)randGenBlockInfoOpcode())) : "7f" + toHex(reasonableInputValues[randGenInputValue()]);
randomCode += randGen() < 32 ? toHex(toCompactBigEndian((uint8_t)randGenBlockInfoOpcode())) : "7f" + toHex(reasonableInputValues[randGenInputValue()]);
continue;
}
uint8_t opcode = randGen();
// disregard all invalid commands, except of one (0x0c)
if ((dev::eth::isValidInstruction(dev::eth::Instruction(opcode)) || (randGen() > 250)))
@ -97,74 +159,58 @@ int main(int argc, char *argv[])
i--;
}
string const s = R"(
{
"randomStatetest" : {
"env" : {
"currentCoinbase" : "945304eb96065b2a98b57a48a06ae28d285a71b5",
"currentDifficulty" : "5623894562375",
"currentGasLimit" : "115792089237316195423570985008687907853269984665640564039457584007913129639935",
"currentNumber" : "0",
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "0",
"code" : "0x6001600101600055",
"nonce" : "0",
"storage" : {
}
},
"945304eb96065b2a98b57a48a06ae28d285a71b5" : {
"balance" : "46",
"code" : "0x6000355415600957005b60203560003555",
"nonce" : "0",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "1000000000000000000",
"code" : "0x",
"nonce" : "0",
"storage" : {
}
}
},
"transaction" : {
"data" : "0x42",
"gasLimit" : "400000",
"gasPrice" : "1",
"nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "100000"
}
}
}
)";
mValue v;
read_string(s, v);
read_string(c_testExample, v);
// insert new random code
v.get_obj().find("randomStatetest")->second.get_obj().find("pre")->second.get_obj().begin()->second.get_obj()["code"] = "0x" + randomCode + (randGen() > 128 ? "55" : "") + (randGen() > 128 ? "60005155" : "");
// insert new data in tx
v.get_obj().find("randomStatetest")->second.get_obj().find("transaction")->second.get_obj()["data"] = "0x" + randomCode;
// insert new value in tx
v.get_obj().find("randomStatetest")->second.get_obj().find("transaction")->second.get_obj()["value"] = toString(randGenUniformInt());
// insert new gasLimit in tx
v.get_obj().find("randomStatetest")->second.get_obj().find("transaction")->second.get_obj()["gasLimit"] = "0x" + toHex(toCompactBigEndian((int)randGenUniformInt()));
// fill test
doStateTests(v);
// stream to output for further handling by the bash script
cout << json_spirit::write_string(v, true);
}
void doRandomCodeAlgo()
{
g_logVerbosity = 0;
dev::test::RandomCodeOptions options;
options.setWeight(dev::eth::Instruction::STOP, 10); //default 50
options.setWeight(dev::eth::Instruction::SSTORE, 70);
options.setWeight(dev::eth::Instruction::CALL, 75);
options.addAddress(Address("0xffffffffffffffffffffffffffffffffffffffff"));
options.addAddress(Address("0x1000000000000000000000000000000000000000"));
options.addAddress(Address("0x095e7baea6a6c7c4c2dfeb977efac326af552d87")); //coinbase
options.addAddress(Address("0x945304eb96065b2a98b57a48a06ae28d285a71b5"));
options.addAddress(Address("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b"));
options.smartCodeProbability = 35;
string randomCode = dev::test::RandomCode::generate(10, options);
string randomData = dev::test::RandomCode::generate(10, options);
mValue v;
read_string(c_testExample, v);
// insert new random code
v.get_obj().find("randomStatetest")->second.get_obj().find("pre")->second.get_obj().begin()->second.get_obj()["code"] = "0x" + randomCode;
// insert new data in tx
v.get_obj().find("randomStatetest")->second.get_obj().find("transaction")->second.get_obj()["data"] = "0x" + randomData;
// insert new value in tx
v.get_obj().find("randomStatetest")->second.get_obj().find("transaction")->second.get_obj()["value"] = dev::test::RandomCode::randomUniIntHex();
// insert new gasLimit in tx
v.get_obj().find("randomStatetest")->second.get_obj().find("transaction")->second.get_obj()["gasLimit"] = dev::test::RandomCode::randomUniIntHex();
// fill test
doStateTests(v);
// stream to output for further handling by the bash script
cout << json_spirit::write_string(v, true);
return 0;
}
void doStateTests(json_spirit::mValue& _v)
@ -179,30 +225,34 @@ void doStateTests(json_spirit::mValue& _v)
assert(o.count("env") > 0);
assert(o.count("pre") > 0);
assert(o.count("transaction") > 0);
test::ImportTest importer(o, true);
eth::State theState = importer.m_statePre;
bytes output;
try
{
output = theState.execute(test::lastHashes(importer.m_environment.currentBlock.number), importer.m_transaction).output;
}
catch (Exception const& _e)
{
cnote << "state execution did throw an exception: " << diagnostic_information(_e);
theState.commit();
}
catch (std::exception const& _e)
{
cnote << "state execution did throw an exception: " << _e.what();
}
test::ImportTest importer(o, true);
eth::State theState = importer.m_statePre;
try
{
output = theState.execute(test::lastHashes(importer.m_environment.currentBlock.number), importer.m_transaction).output;
}
catch (Exception const& _e)
{
cnote << "state execution did throw an exception: " << diagnostic_information(_e);
theState.commit();
}
catch (std::exception const& _e)
{
cnote << "state execution did throw an exception: " << _e.what();
}
#if ETH_FATDB
importer.exportTest(output, theState);
importer.exportTest(output, theState);
#else
cout << "You can not fill tests when FATDB is switched off";
cout << "You can not fill tests when FATDB is switched off";
#endif
}
catch(...)
{
cnote << "Error filling test, probably...";
}
}
}

8
test/fuzzTesting/createRandomVMTest.cpp

@ -155,14 +155,12 @@ void doMyTests(json_spirit::mValue& _v)
}
bytes output;
auto vm = eth::VMFactory::create(fev.gas);
auto vm = eth::VMFactory::create();
u256 gas;
bool vmExceptionOccured = false;
try
{
output = vm->go(fev, fev.simpleTrace()).toBytes();
gas = vm->gas();
output = vm->go(fev.gas, fev, fev.simpleTrace()).toBytes();
}
catch (eth::VMException const& _e)
{
@ -201,7 +199,7 @@ void doMyTests(json_spirit::mValue& _v)
o["post"] = mValue(fev.exportState());
o["callcreates"] = fev.exportCallCreates();
o["out"] = toHex(output, 2, HexPrefix::Add);
o["gas"] = toCompactHex(gas, HexPrefix::Add, 1);
o["gas"] = toCompactHex(fev.gas, HexPrefix::Add, 1);
o["logs"] = test::exportLog(fev.sub.logs);
}
}

224
test/fuzzTesting/fuzzHelper.cpp

@ -0,0 +1,224 @@
/*
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 fuzzHelper.cpp
* @author Dimitry Khokhlov <winsvega@mail.ru>
* @date 2015
*/
#include "fuzzHelper.h"
#include <chrono>
#include <boost/random.hpp>
#include <boost/filesystem/path.hpp>
#include <libevmcore/Instruction.h>
namespace dev
{
namespace test
{
boost::random::mt19937 RandomCode::gen;
boostIntDistrib RandomCode::opCodeDist = boostIntDistrib (0, 255);
boostIntDistrib RandomCode::opLengDist = boostIntDistrib (1, 32);
boostIntDistrib RandomCode::uniIntDist = boostIntDistrib (0, 0x7fffffff);
boostIntGenerator RandomCode::randOpCodeGen = boostIntGenerator(gen, opCodeDist);
boostIntGenerator RandomCode::randOpLengGen = boostIntGenerator(gen, opLengDist);
boostIntGenerator RandomCode::randUniIntGen = boostIntGenerator(gen, uniIntDist);
std::string RandomCode::rndByteSequence(int _length, SizeStrictness _sizeType)
{
refreshSeed();
std::string hash;
_length = (_sizeType == SizeStrictness::Strict) ? std::max(1, _length) : randomUniInt() % _length;
for (auto i = 0; i < _length; i++)
{
uint8_t byte = randOpCodeGen();
hash += toCompactHex(byte);
}
return hash;
}
//generate smart random code
std::string RandomCode::generate(int _maxOpNumber, RandomCodeOptions _options)
{
refreshSeed();
std::string code;
//random opCode amount
boostIntDistrib sizeDist (0, _maxOpNumber);
boostIntGenerator rndSizeGen(gen, sizeDist);
int size = (int)rndSizeGen();
boostWeightGenerator randOpCodeWeight (gen, _options.opCodeProbability);
bool weightsDefined = _options.opCodeProbability.probabilities().size() == 255;
for (auto i = 0; i < size; i++)
{
uint8_t opcode = weightsDefined ? randOpCodeWeight() : randOpCodeGen();
dev::eth::InstructionInfo info = dev::eth::instructionInfo((dev::eth::Instruction) opcode);
if (info.name.find_first_of("INVALID_INSTRUCTION") > 0)
{
//Byte code is yet not implemented
if (_options.useUndefinedOpCodes == false)
{
i--;
continue;
}
}
else
code += fillArguments((dev::eth::Instruction) opcode, _options);
std::string byte = toCompactHex(opcode);
code += (byte == "") ? "00" : byte;
}
return code;
}
std::string RandomCode::randomUniIntHex()
{
refreshSeed();
return "0x" + toCompactHex((int)randUniIntGen());
}
int RandomCode::randomUniInt()
{
refreshSeed();
return (int)randUniIntGen();
}
void RandomCode::refreshSeed()
{
auto now = std::chrono::steady_clock::now().time_since_epoch();
auto timeSinceEpoch = std::chrono::duration_cast<std::chrono::nanoseconds>(now).count();
gen.seed(static_cast<unsigned int>(timeSinceEpoch));
}
std::string RandomCode::getPushCode(std::string const& _hex)
{
int length = _hex.length() / 2;
int pushCode = 96 + length - 1;
return toCompactHex(pushCode) + _hex;
}
std::string RandomCode::getPushCode(int _value)
{
std::string hexString = toCompactHex(_value);
return getPushCode(hexString);
}
std::string RandomCode::fillArguments(dev::eth::Instruction _opcode, RandomCodeOptions const& _options)
{
dev::eth::InstructionInfo info = dev::eth::instructionInfo(_opcode);
std::string code;
bool smart = false;
unsigned num = info.args;
int rand = randUniIntGen() % 100;
if (rand < _options.smartCodeProbability)
smart = true;
if (smart)
{
switch (_opcode)
{
case dev::eth::Instruction::CALL:
//(CALL gaslimit address value memstart1 memlen1 memstart2 memlen2)
code += getPushCode(randUniIntGen() % 32); //memlen2
code += getPushCode(randUniIntGen() % 32); //memstart2
code += getPushCode(randUniIntGen() % 32); //memlen1
code += getPushCode(randUniIntGen() % 32); //memlen1
code += getPushCode(randUniIntGen()); //value
code += getPushCode(toString(_options.getRandomAddress()));//address
code += getPushCode(randUniIntGen()); //gaslimit
break;
default:
smart = false;
}
}
if (smart == false)
for (unsigned i = 0; i < num; i++)
{
//generate random parameters
int length = randOpLengGen();
code += getPushCode(rndByteSequence(length));
}
return code;
}
//Ramdom Code Options
RandomCodeOptions::RandomCodeOptions() : useUndefinedOpCodes(false), smartCodeProbability(50)
{
//each op code with same weight-probability
for (auto i = 0; i < 255; i++)
mapWeights.insert(std::pair<int, int>(i, 50));
setWeights();
}
void RandomCodeOptions::setWeight(dev::eth::Instruction _opCode, int _weight)
{
mapWeights.at((int)_opCode) = _weight;
setWeights();
}
void RandomCodeOptions::addAddress(dev::Address const& _address)
{
addressList.push_back(_address);
}
dev::Address RandomCodeOptions::getRandomAddress() const
{
if (addressList.size() > 0)
{
int index = RandomCode::randomUniInt() % addressList.size();
return addressList[index];
}
return Address(RandomCode::rndByteSequence(20));
}
void RandomCodeOptions::setWeights()
{
std::vector<int> weights;
for (auto const& element: mapWeights)
weights.push_back(element.second);
opCodeProbability = boostDescreteDistrib(weights);
}
BOOST_AUTO_TEST_SUITE(RandomCodeTests)
BOOST_AUTO_TEST_CASE(rndCode)
{
std::string code;
std::cerr << "Testing Random Code: ";
try
{
code = dev::test::RandomCode::generate(10);
}
catch(...)
{
BOOST_ERROR("Exception thrown when generating random code!");
}
std::cerr << code;
}
BOOST_AUTO_TEST_SUITE_END()
}
}

97
test/fuzzTesting/fuzzHelper.h

@ -0,0 +1,97 @@
/*
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 fuzzHelper.h
* @author Dimitry Khokhlov <winsvega@mail.ru>
* @date 2015
*/
#include <string>
#include <boost/random.hpp>
#include <boost/filesystem/path.hpp>
#include <test/TestHelper.h>
#include <libdevcore/CommonIO.h>
#include <libdevcore/CommonData.h>
#include <libevmcore/Instruction.h>
#pragma once
namespace dev
{
namespace test
{
typedef boost::random::uniform_int_distribution<> boostIntDistrib;
typedef boost::random::discrete_distribution<> boostDescreteDistrib;
typedef boost::random::variate_generator<boost::mt19937&, boostIntDistrib > boostIntGenerator;
typedef boost::random::variate_generator<boost::mt19937&, boostDescreteDistrib > boostWeightGenerator;
struct RandomCodeOptions
{
public:
RandomCodeOptions();
void setWeight(dev::eth::Instruction _opCode, int _weight);
void addAddress(dev::Address const& _address);
dev::Address getRandomAddress() const;
bool useUndefinedOpCodes;
int smartCodeProbability;
boostDescreteDistrib opCodeProbability;
private:
void setWeights();
std::map<int, int> mapWeights;
std::vector<dev::Address> addressList;
};
enum class SizeStrictness
{
Strict,
Random
};
class RandomCode
{
public:
/// Generate random vm code
static std::string generate(int _maxOpNumber = 1, RandomCodeOptions _options = RandomCodeOptions());
/// Generate random byte string of a given length
static std::string rndByteSequence(int _length = 1, SizeStrictness _sizeType = SizeStrictness::Strict);
/// Generate random uniForm Int with reasonable value 0..0x7fffffff
static std::string randomUniIntHex();
static int randomUniInt();
private:
static std::string fillArguments(dev::eth::Instruction _opcode, RandomCodeOptions const& _options);
static std::string getPushCode(int _value);
static std::string getPushCode(std::string const& _hex);
static void refreshSeed();
static boost::random::mt19937 gen; ///< Random generator
static boostIntDistrib opCodeDist; ///< 0..255 opcodes
static boostIntDistrib opLengDist; ///< 1..32 byte string
static boostIntDistrib uniIntDist; ///< 0..0x7fffffff
static boostIntGenerator randUniIntGen; ///< Generate random UniformInt from uniIntDist
static boostIntGenerator randOpCodeGen; ///< Generate random value from opCodeDist
static boostIntGenerator randOpLengGen; ///< Generate random length from opLengDist
};
}
}

216
test/libethereum/BlockTestsFiller/bcUncleTestFiller.json

@ -85,6 +85,103 @@
]
},
"UncleIsBrother" : {
"genesisBlockHeader" : {
"bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"coinbase" : "0x8888f1f195afa192cfee860698584c030f4c9db1",
"difficulty" : "131072",
"extraData" : "0x42",
"gasLimit" : "3141592",
"gasUsed" : "0",
"number" : "0",
"parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
"receiptTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"stateRoot" : "0xf99eb1626cfa6db435c0836235942d7ccaa935f1ae247d3f1c21e495685f903a",
"timestamp" : "0x54c98c81",
"mixHash" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"nonce" : "0x0102030405060708",
"transactionsTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"uncleHash" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
},
"expect" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "20"
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"nonce" : "2"
}
},
"pre" : {
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "10000000000000",
"nonce" : "0",
"code" : "",
"storage": {}
}
},
"blocks" : [
{
"transactions" : [
{
"data" : "",
"gasLimit" : "314159",
"gasPrice" : "1",
"nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "10"
}
],
"uncleHeaders" : [
]
},
{
"transactions" : [
{
"data" : "",
"gasLimit" : "314159",
"gasPrice" : "1",
"nonce" : "1",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "10"
}
],
"uncleHeaders" : [
]
},
{
"blockHeader" : {
"bruncle" : "1"
},
"transactions" : [
],
"uncleHeaders" : [
{
"overwriteAndRedoPoW" : "parentHashIsBlocksParent",
"bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"coinbase" : "acde5374fce5edbc8e2a8697c15331677e6ebf0b",
"difficulty" : "131072",
"extraData" : "0x",
"gasLimit" : "4141592",
"gasUsed" : "150000",
"hash" : "9de9879b6a81d1b6c4993c63c90a3c9d1e775f14572694778e828bc64972ae04",
"mixHash" : "b557f905d29ed0fca99d65d0adcce698dee97cf72a13c7cd8d7a7826b8eee770",
"nonce" : "18a524c1790fa83b",
"number" : "2",
"parentHash" : "6134fc6b5d99ee03c4aab1592640f6f9dcbc850668d75d631aee34989b938fae",
"receiptTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"stateRoot" : "ff640b30d613c35dad43e3693329e1b1ee6350f989cf46a288025a1cbfdab9cd",
"timestamp" : "142813170",
"transactionsTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
}
]
}
]
},
"uncleHeaderWithGeneration0" : {
"genesisBlockHeader" : {
"bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
@ -2460,5 +2557,124 @@
]
}
]
},
"EqualUncleInTwoDifferentBlocks" : {
"genesisBlockHeader" : {
"bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"coinbase" : "0x8888f1f195afa192cfee860698584c030f4c9db1",
"difficulty" : "131072",
"extraData" : "0x42",
"gasLimit" : "3141592",
"gasUsed" : "0",
"number" : "0",
"parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
"receiptTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"stateRoot" : "0xf99eb1626cfa6db435c0836235942d7ccaa935f1ae247d3f1c21e495685f903a",
"timestamp" : "0x54c98c81",
"mixHash" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"nonce" : "0x0102030405060708",
"transactionsTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"uncleHash" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
},
"expect" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "20"
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"nonce" : "2"
}
},
"pre" : {
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "10000000000000",
"nonce" : "0",
"code" : "",
"storage": {}
}
},
"blocks" : [
{
"transactions" : [
{
"data" : "",
"gasLimit" : "314159",
"gasPrice" : "1",
"nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "10"
}
],
"uncleHeaders" : [
]
},
{
"transactions" : [
{
"data" : "",
"gasLimit" : "314159",
"gasPrice" : "1",
"nonce" : "1",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "10"
}
],
"uncleHeaders" : [
]
},
{
"transactions" : [
{
"data" : "",
"gasLimit" : "314159",
"gasPrice" : "1",
"nonce" : "2",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "10"
}
],
"uncleHeaders" : [
{
"bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"coinbase" : "bcde5374fce5edbc8e2a8697c15331677e6ebf0b",
"difficulty" : "131072",
"extraData" : "0x",
"gasLimit" : "3141592",
"gasUsed" : "0",
"hash" : "9de9879b6a81d1b6c4993c63c90a3c9d1e775f14572694778e828bc64972ae04",
"mixHash" : "b557f905d29ed0fca99d65d0adcce698dee97cf72a13c7cd8d7a7826b8eee770",
"nonce" : "18a524c1790fa83b",
"number" : "2",
"parentHash" : "6134fc6b5d99ee03c4aab1592640f6f9dcbc850668d75d631aee34989b938fae",
"receiptTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"stateRoot" : "ff640b30d613c35dad43e3693329e1b1ee6350f989cf46a288025a1cbfdab9cd",
"timestamp" : "0x54c98c82",
"transactionsTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
}
]
},
{
"transactions" : [
{
"data" : "",
"gasLimit" : "314159",
"gasPrice" : "1",
"nonce" : "2",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "10"
}
],
"uncleHeaders" : [
{
"sameAsPreviousBlockUncle" : "3"
}
]
}
]
}
}

20
test/libethereum/TransactionTestsFiller/ttTransactionTestFiller.json

@ -63,12 +63,12 @@
}
},
"V_overflow64bit" : {
"V_overflow64bitPlus27" : {
"expect" : "invalid",
"transaction" :
{
"data" : "0x5544",
"gasLimit" : "21000",
"gasLimit" : "22000",
"gasPrice" : "1",
"nonce" : "3",
"to" : "b94f5374fce5edbc8e2a8697c15331677e6ebf0b",
@ -79,6 +79,22 @@
}
},
"V_overflow64bitPlus28" : {
"expect" : "invalid",
"transaction" :
{
"data" : "0x5544",
"gasLimit" : "22000",
"gasPrice" : "1",
"nonce" : "3",
"to" : "b94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"value" : "10",
"v" : "18446744073709551644",
"r" : "0x98ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4a",
"s" : "0x8887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a3"
}
},
"V_overflow64bitSigned" : {
"expect" : "invalid",
"transaction" :

83
test/libethereum/blockchain.cpp

@ -19,7 +19,7 @@
* @date 2015
* block test functions.
*/
#include "test/fuzzTesting/fuzzHelper.h"
#include <boost/filesystem.hpp>
#include <libdevcore/FileSystem.h>
#include <libdevcore/TransientDirectory.h>
@ -33,6 +33,9 @@ using namespace dev::eth;
namespace dev { namespace test {
typedef std::vector<bytes> uncleList;
typedef std::pair<bytes, uncleList> blockSet;
BlockInfo constructBlock(mObject& _o);
bytes createBlockRLPFromFields(mObject& _tObj);
RLPStream createFullBlockFromHeader(BlockInfo const& _bi, bytes const& _txs = RLPEmptyList, bytes const& _uncles = RLPEmptyList);
@ -42,7 +45,7 @@ mObject writeBlockHeaderToJson(mObject& _o, BlockInfo const& _bi);
void overwriteBlockHeader(BlockInfo& _current_BlockHeader, mObject& _blObj);
BlockInfo constructBlock(mObject& _o);
void updatePoW(BlockInfo& _bi);
mArray importUncles(mObject const& blObj, vector<BlockInfo>& vBiUncles, vector<BlockInfo> const& vBiBlocks);
mArray importUncles(mObject const& _blObj, vector<BlockInfo>& _vBiUncles, vector<BlockInfo> const& _vBiBlocks, std::vector<blockSet> _blockSet);
void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
{
@ -65,8 +68,6 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
State trueState(OverlayDB(State::openDB(td_stateDB_tmp.path())), BaseState::Empty, biGenesisBlock.coinbaseAddress);
//Imported blocks from the start
typedef std::vector<bytes> uncleList;
typedef std::pair<bytes, uncleList> blockSet;
std::vector<blockSet> blockSets;
importer.importState(o["pre"].get_obj(), trueState);
@ -138,7 +139,6 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
bc.sync(uncleQueue, state.db(), 4);
bc.attemptImport(block, state.db());
vBiBlocks.push_back(BlockInfo(block));
state.sync(bc);
}
@ -156,7 +156,7 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
//get uncles
vector<BlockInfo> vBiUncles;
blObj["uncleHeaders"] = importUncles(blObj, vBiUncles, vBiBlocks);
blObj["uncleHeaders"] = importUncles(blObj, vBiUncles, vBiBlocks, blockSets);
BlockQueue uncleBlockQueue;
uncleList uncleBlockQueueList;
@ -168,13 +168,14 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
{
uncleBlockQueue.import(&uncle.out(), bc);
uncleBlockQueueList.push_back(uncle.out());
// wait until block is verified
this_thread::sleep_for(chrono::seconds(1));
}
catch(...)
{
cnote << "error in importing uncle! This produces an invalid block (May be by purpose for testing).";
}
}
bc.sync(uncleBlockQueue, state.db(), 4);
state.commitToMine(bc);
@ -214,13 +215,19 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
uncleStream.appendRaw(uncleRlp.out());
}
// update unclehash in case of invalid uncles
current_BlockHeader.sha3Uncles = sha3(uncleStream.out());
updatePoW(current_BlockHeader);
if (blObj.count("blockHeader"))
overwriteBlockHeader(current_BlockHeader, blObj);
if (blObj.count("blockHeader") && blObj["blockHeader"].get_obj().count("bruncle"))
current_BlockHeader.populateFromParent(vBiBlocks[vBiBlocks.size() -1]);
if (vBiUncles.size())
{
// update unclehash in case of invalid uncles
current_BlockHeader.sha3Uncles = sha3(uncleStream.out());
updatePoW(current_BlockHeader);
}
// write block header
mObject oBlockHeader;
writeBlockHeaderToJson(oBlockHeader, current_BlockHeader);
@ -242,7 +249,10 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
blObj["rlp"] = toHex(block2.out(), 2, HexPrefix::Add);
if (sha3(RLP(state.blockData())[0].data()) != sha3(RLP(block2.out())[0].data()))
cnote << "block header mismatch\n";
{
cnote << "block header mismatch state.blockData() vs updated state.info()\n";
cerr << toHex(state.blockData()) << "vs" << toHex(block2.out());
}
if (sha3(RLP(state.blockData())[1].data()) != sha3(RLP(block2.out())[1].data()))
cnote << "txs mismatch\n";
@ -488,36 +498,48 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
// helping functions
mArray importUncles(mObject const& blObj, vector<BlockInfo>& vBiUncles, vector<BlockInfo> const& vBiBlocks)
mArray importUncles(mObject const& _blObj, vector<BlockInfo>& _vBiUncles, vector<BlockInfo> const& _vBiBlocks, std::vector<blockSet> _blockSet)
{
// write uncle list
mArray aUncleList;
mObject uncleHeaderObj_pre;
for (auto const& uHObj: blObj.at("uncleHeaders").get_array())
for (auto const& uHObj: _blObj.at("uncleHeaders").get_array())
{
mObject uncleHeaderObj = uHObj.get_obj();
if (uncleHeaderObj.count("sameAsPreviousSibling"))
{
writeBlockHeaderToJson(uncleHeaderObj_pre, vBiUncles[vBiUncles.size()-1]);
writeBlockHeaderToJson(uncleHeaderObj_pre, _vBiUncles[_vBiUncles.size()-1]);
aUncleList.push_back(uncleHeaderObj_pre);
vBiUncles.push_back(vBiUncles[vBiUncles.size()-1]);
_vBiUncles.push_back(_vBiUncles[_vBiUncles.size()-1]);
uncleHeaderObj_pre = uncleHeaderObj;
continue;
}
if (uncleHeaderObj.count("sameAsBlock"))
{
size_t number = (size_t)toInt(uncleHeaderObj["sameAsBlock"]);
uncleHeaderObj.erase("sameAsBlock");
BlockInfo currentUncle = vBiBlocks[number];
BlockInfo currentUncle = _vBiBlocks[number];
writeBlockHeaderToJson(uncleHeaderObj, currentUncle);
aUncleList.push_back(uncleHeaderObj);
vBiUncles.push_back(currentUncle);
_vBiUncles.push_back(currentUncle);
uncleHeaderObj_pre = uncleHeaderObj;
continue;
}
if (uncleHeaderObj.count("sameAsPreviousBlockUncle"))
{
bytes uncleRLP = _blockSet[(size_t)toInt(uncleHeaderObj["sameAsPreviousBlockUncle"])].second[0];
BlockInfo uncleHeader(uncleRLP);
writeBlockHeaderToJson(uncleHeaderObj, uncleHeader);
aUncleList.push_back(uncleHeaderObj);
_vBiUncles.push_back(uncleHeader);
uncleHeaderObj_pre = uncleHeaderObj;
continue;
}
string overwrite = "false";
if (uncleHeaderObj.count("overwriteAndRedoPoW"))
{
@ -530,12 +552,12 @@ mArray importUncles(mObject const& blObj, vector<BlockInfo>& vBiUncles, vector<B
// make uncle header valid
uncleBlockFromFields.timestamp = (u256)time(0);
cnote << "uncle block n = " << toString(uncleBlockFromFields.number);
if (vBiBlocks.size() > 2)
if (_vBiBlocks.size() > 2)
{
if (uncleBlockFromFields.number - 1 < vBiBlocks.size())
uncleBlockFromFields.populateFromParent(vBiBlocks[(size_t)uncleBlockFromFields.number - 1]);
if (uncleBlockFromFields.number - 1 < _vBiBlocks.size())
uncleBlockFromFields.populateFromParent(_vBiBlocks[(size_t)uncleBlockFromFields.number - 1]);
else
uncleBlockFromFields.populateFromParent(vBiBlocks[vBiBlocks.size() - 2]);
uncleBlockFromFields.populateFromParent(_vBiBlocks[_vBiBlocks.size() - 2]);
}
else
continue;
@ -547,10 +569,14 @@ mArray importUncles(mObject const& blObj, vector<BlockInfo>& vBiUncles, vector<B
uncleBlockFromFields.gasUsed = overwrite == "gasUsed" ? toInt(uncleHeaderObj["gasUsed"]) : uncleBlockFromFields.gasUsed;
uncleBlockFromFields.parentHash = overwrite == "parentHash" ? h256(uncleHeaderObj["parentHash"].get_str()) : uncleBlockFromFields.parentHash;
uncleBlockFromFields.stateRoot = overwrite == "stateRoot" ? h256(uncleHeaderObj["stateRoot"].get_str()) : uncleBlockFromFields.stateRoot;
if (overwrite == "parentHashIsBlocksParent")
uncleBlockFromFields.populateFromParent(_vBiBlocks[_vBiBlocks.size() - 1]);
if (overwrite == "timestamp")
{
uncleBlockFromFields.timestamp = toInt(uncleHeaderObj["timestamp"]);
uncleBlockFromFields.difficulty = uncleBlockFromFields.calculateDifficulty(vBiBlocks[(size_t)uncleBlockFromFields.number - 1]);
uncleBlockFromFields.difficulty = uncleBlockFromFields.calculateDifficulty(_vBiBlocks[(size_t)uncleBlockFromFields.number - 1]);
}
}
@ -558,10 +584,10 @@ mArray importUncles(mObject const& blObj, vector<BlockInfo>& vBiUncles, vector<B
writeBlockHeaderToJson(uncleHeaderObj, uncleBlockFromFields);
aUncleList.push_back(uncleHeaderObj);
vBiUncles.push_back(uncleBlockFromFields);
_vBiUncles.push_back(uncleBlockFromFields);
uncleHeaderObj_pre = uncleHeaderObj;
} //for blObj["uncleHeaders"].get_array()
} //for _blObj["uncleHeaders"].get_array()
return aUncleList;
}
@ -796,6 +822,11 @@ BOOST_AUTO_TEST_CASE(bcGasPricerTest)
dev::test::executeTests("bcGasPricerTest", "/BlockTests",dev::test::getFolder(__FILE__) + "/BlockTestsFiller", dev::test::doBlockchainTests);
}
BOOST_AUTO_TEST_CASE(bcBruncleTest)
{
dev::test::executeTests("bcBruncleTest", "/BlockTests",dev::test::getFolder(__FILE__) + "/BlockTestsFiller", dev::test::doBlockchainTests);
}
BOOST_AUTO_TEST_CASE(bcWalletTest)
{
if (test::Options::get().wallet)

56
test/libevm/VMTestsFiller/vmPushDupSwapTestFiller.json

@ -2446,5 +2446,61 @@
"gasPrice" : "100000000000000",
"gas" : "100000"
}
},
"push32Undefined": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "1000000",
"currentDifficulty" : "256",
"currentTimestamp" : "1",
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "100000000000000000000000",
"nonce" : "0",
"code" : "0x7f010203600055",
"storage": {}
}
},
"exec" : {
"address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
"origin" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"caller" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"value" : "1000000000000000000",
"data" : "",
"gasPrice" : "100000000000000",
"gas" : "100000"
}
},
"push32Undefined2": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "1000000",
"currentDifficulty" : "256",
"currentTimestamp" : "1",
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "100000000000000000000000",
"nonce" : "0",
"code" : "0x7f0102030000000000000000000000000000000000000000000000000000000000600055",
"storage": {}
}
},
"exec" : {
"address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
"origin" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"caller" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"value" : "1000000000000000000",
"data" : "",
"gasPrice" : "100000000000000",
"gas" : "100000"
}
}
}

18
test/libevm/vm.cpp

@ -231,7 +231,7 @@ void FakeExtVM::importCallCreates(mArray& _callcreates)
eth::OnOpFunc FakeExtVM::simpleTrace()
{
return [](uint64_t steps, eth::Instruction inst, bigint newMemSize, bigint gasCost, dev::eth::VM* voidVM, dev::eth::ExtVMFace const* voidExt)
return [](uint64_t steps, eth::Instruction inst, bigint newMemSize, bigint gasCost, bigint gas, dev::eth::VM* voidVM, dev::eth::ExtVMFace const* voidExt)
{
FakeExtVM const& ext = *static_cast<FakeExtVM const*>(voidExt);
eth::VM& vm = *voidVM;
@ -247,7 +247,7 @@ eth::OnOpFunc FakeExtVM::simpleTrace()
o << std::showbase << std::hex << i.first << ": " << i.second << std::endl;
dev::LogOutputStream<eth::VMTraceChannel, false>() << o.str();
dev::LogOutputStream<eth::VMTraceChannel, false>() << " | " << std::dec << ext.depth << " | " << ext.myAddress << " | #" << steps << " | " << std::hex << std::setw(4) << std::setfill('0') << vm.curPC() << " : " << instructionInfo(inst).name << " | " << std::dec << vm.gas() << " | -" << std::dec << gasCost << " | " << newMemSize << "x32" << " ]";
dev::LogOutputStream<eth::VMTraceChannel, false>() << " | " << std::dec << ext.depth << " | " << ext.myAddress << " | #" << steps << " | " << std::hex << std::setw(4) << std::setfill('0') << vm.curPC() << " : " << instructionInfo(inst).name << " | " << std::dec << gas << " | -" << std::dec << gasCost << " | " << newMemSize << "x32" << " ]";
/*creates json stack trace*/
if (eth::VMTraceChannel::verbosity <= g_logVerbosity)
@ -276,7 +276,7 @@ eth::OnOpFunc FakeExtVM::simpleTrace()
/*add all the other details*/
o_step.push_back(Pair("storage", storage));
o_step.push_back(Pair("depth", to_string(ext.depth)));
o_step.push_back(Pair("gas", (string)vm.gas()));
o_step.push_back(Pair("gas", (string)gas));
o_step.push_back(Pair("address", toString(ext.myAddress )));
o_step.push_back(Pair("step", steps ));
o_step.push_back(Pair("pc", (int)vm.curPC()));
@ -324,19 +324,17 @@ void doVMTests(json_spirit::mValue& v, bool _fillin)
}
bytes output;
u256 gas;
bool vmExceptionOccured = false;
try
{
auto vm = eth::VMFactory::create(fev.gas);
auto vm = eth::VMFactory::create();
auto vmtrace = Options::get().vmtrace ? fev.simpleTrace() : OnOpFunc{};
auto outputRef = bytesConstRef{};
{
Listener::ExecTimeGuard guard{i.first};
outputRef = vm->go(fev, vmtrace);
outputRef = vm->go(fev.gas, fev, vmtrace);
}
output = outputRef.toBytes();
gas = vm->gas();
}
catch (VMException const&)
{
@ -391,7 +389,7 @@ void doVMTests(json_spirit::mValue& v, bool _fillin)
o["callcreates"] = fev.exportCallCreates();
o["out"] = output.size() > 4096 ? "#" + toString(output.size()) : toHex(output, 2, HexPrefix::Add);
o["gas"] = toCompactHex(gas, HexPrefix::Add, 1);
o["gas"] = toCompactHex(fev.gas, HexPrefix::Add, 1);
o["logs"] = exportLog(fev.sub.logs);
}
}
@ -414,7 +412,7 @@ void doVMTests(json_spirit::mValue& v, bool _fillin)
checkOutput(output, o);
BOOST_CHECK_EQUAL(toInt(o["gas"]), gas);
BOOST_CHECK_EQUAL(toInt(o["gas"]), fev.gas);
State postState, expectState;
mObject mPostState = fev.exportState();
@ -424,7 +422,7 @@ void doVMTests(json_spirit::mValue& v, bool _fillin)
checkAddresses<std::map<Address, std::tuple<u256, u256, std::map<u256, u256>, bytes> > >(test.addresses, fev.addresses);
checkCallCreates(fev.callcreates, test.callcreates);
checkCallCreates(test.callcreates, fev.callcreates);
checkLog(fev.sub.logs, test.sub.logs);
}

27
test/libsolidity/SolidityABIJSON.cpp

@ -568,6 +568,33 @@ BOOST_AUTO_TEST_CASE(return_param_in_abi)
checkInterface(sourceCode, interface);
}
BOOST_AUTO_TEST_CASE(strings_and_arrays)
{
// bug #1801
char const* sourceCode = R"(
contract test {
function f(string a, bytes b, uint[] c) external {}
}
)";
char const* interface = R"(
[
{
"constant" : false,
"name": "f",
"inputs": [
{ "name": "a", "type": "string" },
{ "name": "b", "type": "bytes" },
{ "name": "c", "type": "uint256[]" }
],
"outputs": [],
"type" : "function"
}
]
)";
checkInterface(sourceCode, interface);
}
BOOST_AUTO_TEST_SUITE_END()
}

192
test/libsolidity/SolidityCompiler.cpp

@ -1,192 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2014
* Unit tests for the solidity compiler.
*/
#include <string>
#include <iostream>
#include <boost/test/unit_test.hpp>
#include <libdevcore/Log.h>
#include <libsolidity/Scanner.h>
#include <libsolidity/Parser.h>
#include <libsolidity/NameAndTypeResolver.h>
#include <libsolidity/Compiler.h>
#include <libsolidity/AST.h>
using namespace std;
using namespace dev::eth;
namespace dev
{
namespace solidity
{
namespace test
{
namespace
{
bytes compileContract(const string& _sourceCode)
{
Parser parser;
ASTPointer<SourceUnit> sourceUnit;
BOOST_REQUIRE_NO_THROW(sourceUnit = parser.parse(make_shared<Scanner>(CharStream(_sourceCode))));
NameAndTypeResolver resolver({});
resolver.registerDeclarations(*sourceUnit);
for (ASTPointer<ASTNode> const& node: sourceUnit->getNodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
{
BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract));
}
for (ASTPointer<ASTNode> const& node: sourceUnit->getNodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
{
BOOST_REQUIRE_NO_THROW(resolver.checkTypeRequirements(*contract));
}
for (ASTPointer<ASTNode> const& node: sourceUnit->getNodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
{
Compiler compiler;
compiler.compileContract(*contract, map<ContractDefinition const*, bytes const*>{});
// debug
//compiler.streamAssembly(cout);
return compiler.getAssembledBytecode();
}
BOOST_FAIL("No contract found in source.");
return bytes();
}
/// Checks that @a _compiledCode is present starting from offset @a _offset in @a _expectation.
/// This is necessary since the compiler will add boilerplate add the beginning that is not
/// tested here.
void checkCodePresentAt(bytes const& _compiledCode, bytes const& _expectation, unsigned _offset)
{
BOOST_REQUIRE(_compiledCode.size() >= _offset + _expectation.size());
auto checkStart = _compiledCode.begin() + _offset;
BOOST_CHECK_EQUAL_COLLECTIONS(checkStart, checkStart + _expectation.size(),
_expectation.begin(), _expectation.end());
}
} // end anonymous namespace
BOOST_AUTO_TEST_SUITE(SolidityCompiler)
BOOST_AUTO_TEST_CASE(smoke_test)
{
char const* sourceCode = "contract test {\n"
" function f() { var x = 2; }\n"
"}\n";
bytes code = compileContract(sourceCode);
unsigned boilerplateSize = 73;
bytes expectation({byte(Instruction::JUMPDEST),
byte(Instruction::PUSH1), 0x0, // initialize local variable x
byte(Instruction::PUSH1), 0x2,
byte(Instruction::SWAP1),
byte(Instruction::POP),
byte(Instruction::JUMPDEST),
byte(Instruction::POP),
byte(Instruction::JUMP)});
checkCodePresentAt(code, expectation, boilerplateSize);
}
BOOST_AUTO_TEST_CASE(ifStatement)
{
char const* sourceCode = "contract test {\n"
" function f() { bool x; if (x) 77; else if (!x) 78; else 79; }"
"}\n";
bytes code = compileContract(sourceCode);
unsigned shift = 60;
unsigned boilerplateSize = 73;
bytes expectation({
byte(Instruction::JUMPDEST),
byte(Instruction::PUSH1), 0x0,
byte(Instruction::DUP1),
byte(Instruction::ISZERO),
byte(Instruction::PUSH1), byte(0x0f + shift), // "false" target
byte(Instruction::JUMPI),
// "if" body
byte(Instruction::PUSH1), 0x4d,
byte(Instruction::POP),
byte(Instruction::PUSH1), byte(0x21 + shift),
byte(Instruction::JUMP),
// new check "else if" condition
byte(Instruction::JUMPDEST),
byte(Instruction::DUP1),
byte(Instruction::ISZERO),
byte(Instruction::ISZERO),
byte(Instruction::PUSH1), byte(0x1c + shift),
byte(Instruction::JUMPI),
// "else if" body
byte(Instruction::PUSH1), 0x4e,
byte(Instruction::POP),
byte(Instruction::PUSH1), byte(0x20 + shift),
byte(Instruction::JUMP),
// "else" body
byte(Instruction::JUMPDEST),
byte(Instruction::PUSH1), 0x4f,
byte(Instruction::POP),
});
checkCodePresentAt(code, expectation, boilerplateSize);
}
BOOST_AUTO_TEST_CASE(loops)
{
char const* sourceCode = "contract test {\n"
" function f() { while(true){1;break;2;continue;3;return;4;} }"
"}\n";
bytes code = compileContract(sourceCode);
unsigned shift = 60;
unsigned boilerplateSize = 73;
bytes expectation({byte(Instruction::JUMPDEST),
byte(Instruction::JUMPDEST),
byte(Instruction::PUSH1), 0x1,
byte(Instruction::ISZERO),
byte(Instruction::PUSH1), byte(0x21 + shift),
byte(Instruction::JUMPI),
byte(Instruction::PUSH1), 0x1,
byte(Instruction::POP),
byte(Instruction::PUSH1), byte(0x21 + shift),
byte(Instruction::JUMP), // break
byte(Instruction::PUSH1), 0x2,
byte(Instruction::POP),
byte(Instruction::PUSH1), byte(0x2 + shift),
byte(Instruction::JUMP), // continue
byte(Instruction::PUSH1), 0x3,
byte(Instruction::POP),
byte(Instruction::PUSH1), byte(0x22 + shift),
byte(Instruction::JUMP), // return
byte(Instruction::PUSH1), 0x4,
byte(Instruction::POP),
byte(Instruction::PUSH1), byte(0x2 + shift),
byte(Instruction::JUMP),
byte(Instruction::JUMPDEST),
byte(Instruction::JUMPDEST),
byte(Instruction::JUMP)});
checkCodePresentAt(code, expectation, boilerplateSize);
}
BOOST_AUTO_TEST_SUITE_END()
}
}
} // end namespaces

66
test/libsolidity/SolidityEndToEndTest.cpp

@ -4080,7 +4080,6 @@ BOOST_AUTO_TEST_CASE(struct_delete_member)
}
)";
compileAndRun(sourceCode, 0, "test");
auto res = callContractFunction("deleteMember()");
BOOST_CHECK(callContractFunction("deleteMember()") == encodeArgs(0));
}
@ -4106,10 +4105,73 @@ BOOST_AUTO_TEST_CASE(struct_delete_struct_in_mapping)
}
)";
compileAndRun(sourceCode, 0, "test");
auto res = callContractFunction("deleteIt()");
BOOST_CHECK(callContractFunction("deleteIt()") == encodeArgs(0));
}
BOOST_AUTO_TEST_CASE(evm_exceptions_out_of_band_access)
{
char const* sourceCode = R"(
contract A {
uint[3] arr;
bool public test = false;
function getElement(uint i) returns (uint)
{
return arr[i];
}
function testIt() returns (bool)
{
uint i = this.getElement(5);
test = true;
return true;
}
}
)";
compileAndRun(sourceCode, 0, "A");
BOOST_CHECK(callContractFunction("test()") == encodeArgs(false));
BOOST_CHECK(callContractFunction("testIt()") == encodeArgs());
BOOST_CHECK(callContractFunction("test()") == encodeArgs(false));
}
BOOST_AUTO_TEST_CASE(evm_exceptions_in_constructor_call_fail)
{
char const* sourceCode = R"(
contract A {
function A()
{
this.call("123");
}
}
contract B {
uint public test = 1;
function testIt()
{
A a = new A();
++test;
}
}
)";
compileAndRun(sourceCode, 0, "B");
BOOST_CHECK(callContractFunction("testIt()") == encodeArgs());
BOOST_CHECK(callContractFunction("test()") == encodeArgs(2));
}
BOOST_AUTO_TEST_CASE(evm_exceptions_in_constructor_out_of_baund)
{
char const* sourceCode = R"(
contract A {
uint public test = 1;
uint[3] arr;
function A()
{
test = arr[5];
++test;
}
}
)";
BOOST_CHECK(compileAndRunWthoutCheck(sourceCode, 0, "A").empty());
}
BOOST_AUTO_TEST_SUITE_END()
}

33
test/libsolidity/SolidityNameAndTypeResolution.cpp

@ -1783,6 +1783,39 @@ BOOST_AUTO_TEST_CASE(uninitialized_var)
BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError);
}
BOOST_AUTO_TEST_CASE(string)
{
char const* sourceCode = R"(
contract C {
string s;
function f(string x) external { s = x; }
}
)";
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(sourceCode));
}
BOOST_AUTO_TEST_CASE(string_index)
{
char const* sourceCode = R"(
contract C {
string s;
function f() { var a = s[2]; }
}
)";
BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError);
}
BOOST_AUTO_TEST_CASE(string_length)
{
char const* sourceCode = R"(
contract C {
string s;
function f() { var a = s.length; }
}
)";
BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError);
}
BOOST_AUTO_TEST_SUITE_END()
}

32
test/libsolidity/SolidityOptimizer.cpp

@ -1004,6 +1004,38 @@ BOOST_AUTO_TEST_CASE(block_deduplicator)
BOOST_CHECK_EQUAL(pushTags.size(), 2);
}
BOOST_AUTO_TEST_CASE(block_deduplicator_loops)
{
AssemblyItems input{
u256(0),
eth::Instruction::SLOAD,
AssemblyItem(PushTag, 1),
AssemblyItem(PushTag, 2),
eth::Instruction::JUMPI,
eth::Instruction::JUMP,
AssemblyItem(Tag, 1),
u256(5),
u256(6),
eth::Instruction::SSTORE,
AssemblyItem(PushTag, 1),
eth::Instruction::JUMP,
AssemblyItem(Tag, 2),
u256(5),
u256(6),
eth::Instruction::SSTORE,
AssemblyItem(PushTag, 2),
eth::Instruction::JUMP,
};
BlockDeduplicator dedup(input);
dedup.deduplicate();
set<u256> pushTags;
for (AssemblyItem const& item: input)
if (item.type() == PushTag)
pushTags.insert(item.data());
BOOST_CHECK_EQUAL(pushTags.size(), 1);
}
BOOST_AUTO_TEST_SUITE_END()
}

43
test/libsolidity/solidityExecutionFramework.h

@ -42,21 +42,25 @@ class ExecutionFramework
public:
ExecutionFramework() { g_logVerbosity = 0; }
bytes const& compileAndRun(std::string const& _sourceCode, u256 const& _value = 0, std::string const& _contractName = "")
bytes const& compileAndRunWthoutCheck(std::string const& _sourceCode, u256 const& _value = 0, std::string const& _contractName = "")
{
m_compiler.reset(false, m_addStandardSources);
m_compiler.addSource("", _sourceCode);
ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(m_optimize), "Compiling contract failed");
bytes code = m_compiler.getBytecode(_contractName);
sendMessage(code, true, _value);
return m_output;
}
bytes const& compileAndRun(std::string const& _sourceCode, u256 const& _value = 0, std::string const& _contractName = "")
{
compileAndRunWthoutCheck(_sourceCode, _value, _contractName);
BOOST_REQUIRE(!m_output.empty());
return m_output;
}
template <class... Args>
bytes const& callContractFunctionWithValue(std::string _sig, u256 const& _value,
Args const&... _arguments)
bytes const& callContractFunctionWithValue(std::string _sig, u256 const& _value, Args const&... _arguments)
{
FixedHash<4> hash(dev::sha3(_sig));
sendMessage(hash.asBytes() + encodeArgs(_arguments...), false, _value);
@ -74,21 +78,30 @@ public:
{
bytes solidityResult = callContractFunction(_sig, _arguments...);
bytes cppResult = callCppAndEncodeResult(_cppFunction, _arguments...);
BOOST_CHECK_MESSAGE(solidityResult == cppResult, "Computed values do not match."
"\nSolidity: " + toHex(solidityResult) + "\nC++: " + toHex(cppResult));
BOOST_CHECK_MESSAGE(
solidityResult == cppResult,
"Computed values do not match.\nSolidity: " +
toHex(solidityResult) +
"\nC++: " +
toHex(cppResult));
}
template <class CppFunction, class... Args>
void testSolidityAgainstCppOnRange(std::string _sig, CppFunction const& _cppFunction,
u256 const& _rangeStart, u256 const& _rangeEnd)
void testSolidityAgainstCppOnRange(std::string _sig, CppFunction const& _cppFunction, u256 const& _rangeStart, u256 const& _rangeEnd)
{
for (u256 argument = _rangeStart; argument < _rangeEnd; ++argument)
{
bytes solidityResult = callContractFunction(_sig, argument);
bytes cppResult = callCppAndEncodeResult(_cppFunction, argument);
BOOST_CHECK_MESSAGE(solidityResult == cppResult, "Computed values do not match."
"\nSolidity: " + toHex(solidityResult) + "\nC++: " + toHex(cppResult) +
"\nArgument: " + toHex(encode(argument)));
BOOST_CHECK_MESSAGE(
solidityResult == cppResult,
"Computed values do not match.\nSolidity: " +
toHex(solidityResult) +
"\nC++: " +
toHex(cppResult) +
"\nArgument: " +
toHex(encode(argument))
);
}
}
@ -135,8 +148,10 @@ protected:
{
m_state.addBalance(m_sender, _value); // just in case
eth::Executive executive(m_state, eth::LastHashes(), 0);
eth::Transaction t = _isCreation ? eth::Transaction(_value, m_gasPrice, m_gas, _data, 0, KeyPair::create().sec())
: eth::Transaction(_value, m_gasPrice, m_gas, m_contractAddress, _data, 0, KeyPair::create().sec());
eth::Transaction t =
_isCreation ?
eth::Transaction(_value, m_gasPrice, m_gas, _data, 0, KeyPair::create().sec()) :
eth::Transaction(_value, m_gasPrice, m_gas, m_contractAddress, _data, 0, KeyPair::create().sec());
bytes transactionRLP = t.rlp();
try
{
@ -155,7 +170,7 @@ protected:
else
{
BOOST_REQUIRE(m_state.addressHasCode(m_contractAddress));
BOOST_REQUIRE(!executive.call(m_contractAddress, m_contractAddress, m_sender, _value, m_gasPrice, &_data, m_gas, m_sender));
BOOST_REQUIRE(!executive.call(m_contractAddress, m_sender, _value, m_gasPrice, &_data, m_gas));
}
BOOST_REQUIRE(executive.go());
m_state.noteSending(m_sender);

8
test/libwhisper/whisperMessage.cpp

@ -34,9 +34,9 @@ struct VerbosityHolder
int oldLogVerbosity;
};
FullTopic createRandomTopics(unsigned int i)
Topics createRandomTopics(unsigned int i)
{
FullTopic ret;
Topics ret;
h256 t(i);
for (int j = 0; j < 8; ++j)
@ -72,14 +72,14 @@ void comparePayloads(Message const& m1, Message const& m2)
void sealAndOpenSingleMessage(unsigned int i)
{
Secret zero;
FullTopic topics = createRandomTopics(i);
Topics topics = createRandomTopics(i);
bytes const payload = createRandomPayload(i);
Message m1(payload);
Envelope e = m1.seal(zero, topics, 1, 1);
for (auto const& t: topics)
{
FullTopic singleTopic;
Topics singleTopic;
singleTopic.push_back(t);
Message m2(e, singleTopic, zero);
comparePayloads(m1, m2);

Loading…
Cancel
Save