Browse Source

Merge remote-tracking branch 'upstream/develop' into expandEthCLI

Conflicts:
	eth/main.cpp
	libethereum/Client.h
cl-refactor
CJentzsch 10 years ago
parent
commit
c451d3a7fa
  1. 1
      CMakeLists.txt
  2. 9
      alethzero/MainWin.cpp
  3. 2
      alethzero/MainWin.h
  4. 6
      alethzero/NatspecHandler.h
  5. 2
      cmake/EthCompilerSettings.cmake
  6. 10
      eth/main.cpp
  7. 3
      ethminer/CMakeLists.txt
  8. 9
      ethminer/MinerAux.h
  9. 19
      ethvm/CMakeLists.txt
  10. 200
      ethvm/main.cpp
  11. 2
      evmjit/libevmjit-cpp/JitVM.cpp
  12. 43
      evmjit/libevmjit/Array.cpp
  13. 27
      evmjit/libevmjit/Cache.cpp
  14. 5
      json_spirit/json_spirit_writer_template.h
  15. 2
      libdevcore/Common.cpp
  16. 2
      libdevcore/Common.h
  17. 39
      libdevcore/Exceptions.h
  18. 8
      libdevcore/Log.cpp
  19. 3
      libdevcore/Log.h
  20. 43
      libdevcore/MemoryDB.cpp
  21. 8
      libdevcore/MemoryDB.h
  22. 8
      libdevcore/RangeMask.h
  23. 3
      libdevcore/vector_ref.h
  24. 4
      libdevcrypto/Common.cpp
  25. 4
      libdevcrypto/CryptoPP.cpp
  26. 2
      libdevcrypto/CryptoPP.h
  27. 10
      libdevcrypto/OverlayDB.cpp
  28. 203
      libethash-cl/ethash_cl_miner.cpp
  29. 17
      libethash-cl/ethash_cl_miner.h
  30. 24
      libethcore/BlockInfo.cpp
  31. 29
      libethcore/Common.cpp
  32. 6
      libethcore/Common.h
  33. 25
      libethcore/Ethash.cpp
  34. 8
      libethcore/Ethash.h
  35. 77
      libethcore/Exceptions.h
  36. 267
      libethereum/BlockChain.cpp
  37. 12
      libethereum/BlockChain.h
  38. 274
      libethereum/BlockQueue.cpp
  39. 38
      libethereum/BlockQueue.h
  40. 11
      libethereum/CMakeLists.txt
  41. 280
      libethereum/Client.cpp
  42. 33
      libethereum/Client.h
  43. 38
      libethereum/ClientBase.cpp
  44. 13
      libethereum/ClientBase.h
  45. 11
      libethereum/CommonNet.h
  46. 7
      libethereum/DownloadMan.cpp
  47. 8
      libethereum/DownloadMan.h
  48. 214
      libethereum/EthereumHost.cpp
  49. 16
      libethereum/EthereumHost.h
  50. 50
      libethereum/EthereumPeer.cpp
  51. 20
      libethereum/EthereumPeer.h
  52. 141
      libethereum/Executive.cpp
  53. 27
      libethereum/Executive.h
  54. 4
      libethereum/ExtVM.h
  55. 3
      libethereum/Interface.h
  56. 27
      libethereum/LogFilter.cpp
  57. 15
      libethereum/LogFilter.h
  58. 8
      libethereum/Precompiled.cpp
  59. 31
      libethereum/Sentinel.h
  60. 3
      libethereum/Sentinel.json
  61. 264
      libethereum/State.cpp
  62. 33
      libethereum/State.h
  63. 64
      libethereum/Transaction.cpp
  64. 9
      libethereum/Transaction.h
  65. 53
      libethereum/VerifiedBlock.h
  66. 3
      libevm/ExtVMFace.cpp
  67. 29
      libevm/ExtVMFace.h
  68. 322
      libevm/VM.cpp
  69. 6
      libevm/VM.h
  70. 13
      libevm/VMFace.h
  71. 2
      libevmasm/Assembly.cpp
  72. 21
      libevmasm/CommonSubexpressionEliminator.cpp
  73. 4
      libevmcore/Exceptions.h
  74. 2
      libjsqrc/ethereumjs/.jshintrc
  75. 7
      libjsqrc/ethereumjs/.versions
  76. 5
      libjsqrc/ethereumjs/bower.json
  77. 1915
      libjsqrc/ethereumjs/dist/web3-light.js
  78. 71
      libjsqrc/ethereumjs/dist/web3-light.js.map
  79. 4
      libjsqrc/ethereumjs/dist/web3-light.min.js
  80. 1917
      libjsqrc/ethereumjs/dist/web3.js
  81. 71
      libjsqrc/ethereumjs/dist/web3.js.map
  82. 4
      libjsqrc/ethereumjs/dist/web3.min.js
  83. 23
      libjsqrc/ethereumjs/example/contract.html
  84. 27
      libjsqrc/ethereumjs/example/event_inc.html
  85. 203
      libjsqrc/ethereumjs/example/icap.html
  86. 102
      libjsqrc/ethereumjs/example/namereg.html
  87. 76
      libjsqrc/ethereumjs/example/natspec_contract.html
  88. 2
      libjsqrc/ethereumjs/index.js
  89. 6
      libjsqrc/ethereumjs/lib/solidity/param.js
  90. 48
      libjsqrc/ethereumjs/lib/utils/config.js
  91. 39
      libjsqrc/ethereumjs/lib/utils/sha3.js
  92. 85
      libjsqrc/ethereumjs/lib/utils/utils.js
  93. 2
      libjsqrc/ethereumjs/lib/version.json
  94. 13
      libjsqrc/ethereumjs/lib/web3.js
  95. 3
      libjsqrc/ethereumjs/lib/web3/event.js
  96. 31
      libjsqrc/ethereumjs/lib/web3/function.js
  97. 108
      libjsqrc/ethereumjs/lib/web3/icap.js
  98. 46
      libjsqrc/ethereumjs/lib/web3/namereg.js
  99. 94
      libjsqrc/ethereumjs/lib/web3/transfer.js
  100. 2
      libjsqrc/ethereumjs/package.js

1
CMakeLists.txt

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

9
alethzero/MainWin.cpp

@ -240,6 +240,10 @@ Main::Main(QWidget *parent) :
#if !ETH_FATDB
removeDockWidget(ui->dockWidget_accounts);
#endif
#if !ETH_EVMJIT
ui->jitvm->setEnabled(false);
ui->jitvm->setChecked(false);
#endif
installWatches();
startTimer(100);
@ -805,6 +809,7 @@ void Main::readSettings(bool _skipGeometry)
ui->usePrivate->setChecked(m_privateChain.size());
ui->verbosity->setValue(s.value("verbosity", 1).toInt());
ui->jitvm->setChecked(s.value("jitvm", true).toBool());
on_jitvm_triggered();
ui->urlEdit->setText(s.value("url", "about:blank").toString()); //http://gavwood.com/gavcoin.html
on_urlEdit_returnPressed();
@ -1240,7 +1245,9 @@ void Main::refreshBlockCount()
{
auto d = ethereum()->blockChain().details();
BlockQueueStatus b = ethereum()->blockQueueStatus();
ui->chainStatus->setText(QString("%3 ready %4 verifying %5 unverified %6 future %7 unknown %8 bad %1 #%2").arg(m_privateChain.size() ? "[" + m_privateChain + "] " : "testnet").arg(d.number).arg(b.verified).arg(b.verifying).arg(b.unverified).arg(b.future).arg(b.unknown).arg(b.bad));
HashChainStatus h = ethereum()->hashChainStatus();
ui->chainStatus->setText(QString("%10/%11%12 hashes %3 importing %4 ready %5 verifying %6 unverified %7 future %8 unknown %9 bad %1 #%2")
.arg(m_privateChain.size() ? "[" + m_privateChain + "] " : "testnet").arg(d.number).arg(b.importing).arg(b.verified).arg(b.verifying).arg(b.unverified).arg(b.future).arg(b.unknown).arg(b.bad).arg(h.received).arg(h.estimated ? "~" : "").arg(h.total));
}
void Main::on_turboMining_triggered()

2
alethzero/MainWin.h

@ -238,7 +238,7 @@ private:
void installNameRegWatch();
void installBalancesWatch();
virtual void timerEvent(QTimerEvent*);
virtual void timerEvent(QTimerEvent*) override;
void refreshNetwork();
void refreshMining();

6
alethzero/NatspecHandler.h

@ -39,17 +39,17 @@ class NatspecHandler: public NatSpecFace
~NatspecHandler();
/// Stores locally in a levelDB a key value pair of contract code hash to natspec documentation
void add(dev::h256 const& _contractHash, std::string const& _doc);
virtual void add(dev::h256 const& _contractHash, std::string const& _doc) override;
/// Retrieves the natspec documentation as a string given a contract code hash
std::string retrieve(dev::h256 const& _contractHash) const override;
/// Given a json natspec string and the transaction data return the user notice
std::string getUserNotice(std::string const& json, const dev::bytes& _transactionData);
virtual std::string getUserNotice(std::string const& json, const dev::bytes& _transactionData) override;
/// Given a contract code hash and the transaction's data retrieve the natspec documention's
/// user notice for that transaction.
/// @returns The user notice or an empty string if no natspec for the contract exists
/// or if the existing natspec does not document the @c _methodName
std::string getUserNotice(dev::h256 const& _contractHash, dev::bytes const& _transactionDacta);
virtual std::string getUserNotice(dev::h256 const& _contractHash, dev::bytes const& _transactionDacta) override;
private:
ldb::ReadOptions m_readOptions;

2
cmake/EthCompilerSettings.cmake

@ -21,7 +21,7 @@ elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g -DETH_DEBUG")
set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG -DETH_RELEASE")
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG -DETH_RELEASE")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g -DETH_DEBUG")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g -DETH_RELEASE")
if ("${CMAKE_SYSTEM_NAME}" MATCHES "Linux")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libstdc++ -fcolor-diagnostics -Qunused-arguments -DBOOST_ASIO_HAS_CLANG_LIBCXX")

10
eth/main.cpp

@ -135,6 +135,7 @@ void help()
<< " --session-sign-key <address> Sign all transactions with the key of the given address for this session only." << endl
<< " --master <password> Give the master password for the key store." << endl
<< " --password <password> Give a password for a private key." << endl
<< " --sentinel <server> Set the sentinel for reporting bad blocks or chain issues." << endl
<< endl
<< "Client transacting:" << endl
/*<< " -B,--block-fees <n> Set the block fee profit in the reference unit e.g. ¢ (default: 15)." << endl
@ -147,6 +148,7 @@ void help()
<< " -a,--address <addr> Set the coinbase (mining payout) address to addr (default: auto)." << endl
<< " -m,--mining <on/off/number> Enable mining, optionally for a specified number of blocks (default: off)" << endl
<< " -f,--force-mining Mine even when there are no transactions to mine (default: off)" << endl
<< " --mine-on-wrong-chain Mine even when we know it's the wrong chain (default: off)" << endl
<< " -C,--cpu When mining, use the CPU." << endl
<< " -G,--opencl When mining use the GPU via OpenCL." << endl
<< " --opencl-platform <n> When mining using -G/--opencl use OpenCL platform n (default: 0)." << endl
@ -288,6 +290,7 @@ int main(int argc, char** argv)
bool upnp = true;
WithExisting killChain = WithExisting::Trust;
bool jit = false;
string sentinel;
/// Networking params.
string clientName;
@ -303,6 +306,7 @@ int main(int argc, char** argv)
/// Mining params
unsigned mining = 0;
bool forceMining = false;
bool mineOnWrongChain = false;
Address signingKey;
Address sessionKey;
Address beneficiary = signingKey;
@ -385,6 +389,10 @@ int main(int argc, char** argv)
mode = OperationMode::Export;
filename = argv[++i];
}
else if (arg == "--sentinel" && i + 1 < argc)
sentinel = argv[++i];
else if (arg == "--mine-on-wrong-chain")
mineOnWrongChain = true;
else if (arg == "--format" && i + 1 < argc)
{
string m = argv[++i];
@ -680,6 +688,8 @@ int main(int argc, char** argv)
nodeMode == NodeMode::Full ? set<string>{"eth"/*, "shh"*/} : set<string>(),
netPrefs,
&nodesState);
web3.ethereum()->setMineOnBadChain(mineOnWrongChain);
web3.ethereum()->setSentinel(sentinel);
auto toNumber = [&](string const& s) -> unsigned {
if (s == "latest")

3
ethminer/CMakeLists.txt

@ -5,7 +5,10 @@ aux_source_directory(. SRC_LIST)
include_directories(BEFORE ..)
include_directories(${Boost_INCLUDE_DIRS})
if (JSONRPC)
include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS})
include_directories(${JSON_RPC_CPP_INCLUDE_DIRS})
endif()
set(EXECUTABLE ethminer)

9
ethminer/MinerAux.h

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

19
ethvm/CMakeLists.txt

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

200
ethvm/main.cpp

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

2
evmjit/libevmjit-cpp/JitVM.cpp

@ -51,7 +51,7 @@ bytesConstRef JitVM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _on
m_data.timestamp = static_cast<decltype(m_data.timestamp)>(_ext.currentBlock.timestamp);
m_data.code = _ext.code.data();
m_data.codeSize = _ext.code.size();
m_data.codeHash = eth2llvm(sha3(_ext.code));
m_data.codeHash = eth2llvm(_ext.codeHash);
auto env = reinterpret_cast<Env*>(&_ext);
auto exitCode = m_engine.run(&m_data, env);

43
evmjit/libevmjit/Array.cpp

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

27
evmjit/libevmjit/Cache.cpp

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

5
json_spirit/json_spirit_writer_template.h

@ -25,13 +25,9 @@ namespace json_spirit
return 'A' - 10 + ch;
}
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-local-typedefs")
template< class String_type >
String_type non_printable_to_string( unsigned int c )
{
typedef typename String_type::value_type Char_type;
String_type result( 6, '\\' );
result[1] = 'u';
@ -43,7 +39,6 @@ namespace json_spirit
return result;
}
#pragma GCC diagnostic pop
template< typename Char_type, class String_type >
bool add_esc_char( Char_type c, String_type& s )

2
libdevcore/Common.cpp

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

2
libdevcore/Common.h

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

39
libdevcore/Exceptions.h

@ -30,7 +30,8 @@
namespace dev
{
// base class for all exceptions
/// Base class for all exceptions.
struct Exception: virtual std::exception, virtual boost::exception
{
Exception(std::string _message = std::string()): m_message(std::move(_message)) {}
@ -40,20 +41,26 @@ private:
std::string m_message;
};
struct BadHexCharacter: virtual Exception {};
struct RLPException: virtual Exception {};
struct BadCast: virtual RLPException {};
struct BadRLP: virtual RLPException {};
struct OversizeRLP: virtual RLPException {};
struct UndersizeRLP: virtual RLPException {};
struct NoNetworking: virtual Exception {};
struct NoUPnPDevice: virtual Exception {};
struct RootNotFound: virtual Exception {};
struct BadRoot: virtual Exception {};
struct FileError: virtual Exception {};
struct Overflow: virtual Exception {};
#define DEV_SIMPLE_EXCEPTION(X) struct X: virtual Exception { const char* what() const noexcept override { return #X; } }
/// Base class for all RLP exceptions.
struct RLPException: virtual Exception { RLPException(std::string _message = std::string()): Exception(_message) {} };
#define DEV_SIMPLE_EXCEPTION_RLP(X) struct X: virtual RLPException { const char* what() const noexcept override { return #X; } }
DEV_SIMPLE_EXCEPTION_RLP(BadCast);
DEV_SIMPLE_EXCEPTION_RLP(BadRLP);
DEV_SIMPLE_EXCEPTION_RLP(OversizeRLP);
DEV_SIMPLE_EXCEPTION_RLP(UndersizeRLP);
DEV_SIMPLE_EXCEPTION(BadHexCharacter);
DEV_SIMPLE_EXCEPTION(NoNetworking);
DEV_SIMPLE_EXCEPTION(NoUPnPDevice);
DEV_SIMPLE_EXCEPTION(RootNotFound);
DEV_SIMPLE_EXCEPTION(BadRoot);
DEV_SIMPLE_EXCEPTION(FileError);
DEV_SIMPLE_EXCEPTION(Overflow);
DEV_SIMPLE_EXCEPTION(FailedInvariant);
struct InterfaceNotSupported: virtual Exception { public: InterfaceNotSupported(std::string _f): Exception("Interface " + _f + " not supported.") {} };
struct FailedInvariant: virtual Exception {};
struct ExternalFunctionFailure: virtual Exception { public: ExternalFunctionFailure(std::string _f): Exception("Function " + _f + "() failed.") {} };
// error information to be added to exceptions
@ -66,5 +73,7 @@ using errinfo_min = boost::error_info<struct tag_min, bigint>;
using errinfo_max = boost::error_info<struct tag_max, bigint>;
using RequirementError = boost::tuple<errinfo_required, errinfo_got>;
using errinfo_hash256 = boost::error_info<struct tag_hash, h256>;
using HashMismatchError = boost::tuple<errinfo_hash256, errinfo_hash256>;
using errinfo_required_h256 = boost::error_info<struct tag_required_h256, h256>;
using errinfo_got_h256 = boost::error_info<struct tag_get_h256, h256>;
using Hash256RequirementError = boost::tuple<errinfo_required_h256, errinfo_got_h256>;
}

8
libdevcore/Log.cpp

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

3
libdevcore/Log.h

@ -73,6 +73,9 @@ public:
LogOverride(bool _value): LogOverrideAux(&typeid(Channel), _value) {}
};
bool isChannelVisible(std::type_info const* _ch, bool _default);
template <class Channel> bool isChannelVisible() { return isChannelVisible(&typeid(Channel), Channel::verbosity <= g_logVerbosity); }
/// Temporary changes system's verbosity for specific function. Restores the old verbosity when function returns.
/// Not thread-safe, use with caution!
struct VerbosityHolder

43
libdevcore/MemoryDB.cpp

@ -32,7 +32,9 @@ const char* DBWarn::name() { return "TDB"; }
std::unordered_map<h256, std::string> MemoryDB::get() const
{
#if DEV_GUARDED_DB
ReadGuard l(x_this);
#endif
std::unordered_map<h256, std::string> ret;
for (auto const& i: m_main)
if (!m_enforceRefs || i.second.second > 0)
@ -44,8 +46,10 @@ MemoryDB& MemoryDB::operator=(MemoryDB const& _c)
{
if (this == &_c)
return *this;
#if DEV_GUARDED_DB
ReadGuard l(_c.x_this);
WriteGuard l2(x_this);
#endif
m_main = _c.m_main;
m_aux = _c.m_aux;
return *this;
@ -53,7 +57,9 @@ MemoryDB& MemoryDB::operator=(MemoryDB const& _c)
std::string MemoryDB::lookup(h256 const& _h) const
{
#if DEV_GUARDED_DB
ReadGuard l(x_this);
#endif
auto it = m_main.find(_h);
if (it != m_main.end())
{
@ -67,7 +73,9 @@ std::string MemoryDB::lookup(h256 const& _h) const
bool MemoryDB::exists(h256 const& _h) const
{
#if DEV_GUARDED_DB
ReadGuard l(x_this);
#endif
auto it = m_main.find(_h);
if (it != m_main.end() && (!m_enforceRefs || it->second.second > 0))
return true;
@ -76,7 +84,9 @@ bool MemoryDB::exists(h256 const& _h) const
void MemoryDB::insert(h256 const& _h, bytesConstRef _v)
{
#if DEV_GUARDED_DB
WriteGuard l(x_this);
#endif
auto it = m_main.find(_h);
if (it != m_main.end())
{
@ -92,7 +102,9 @@ void MemoryDB::insert(h256 const& _h, bytesConstRef _v)
bool MemoryDB::kill(h256 const& _h)
{
#if DEV_GUARDED_DB
ReadGuard l(x_this);
#endif
if (m_main.count(_h))
{
if (m_main[_h].second > 0)
@ -117,9 +129,38 @@ bool MemoryDB::kill(h256 const& _h)
return false;
}
bytes MemoryDB::lookupAux(h256 const& _h) const
{
#if DEV_GUARDED_DB
ReadGuard l(x_this);
#endif
auto it = m_aux.find(_h);
if (it != m_aux.end() && (!m_enforceRefs || it->second.second))
return it->second.first;
return bytes();
}
void MemoryDB::removeAux(h256 const& _h)
{
#if DEV_GUARDED_DB
WriteGuard l(x_this);
#endif
m_aux[_h].second = false;
}
void MemoryDB::insertAux(h256 const& _h, bytesConstRef _v)
{
#if DEV_GUARDED_DB
WriteGuard l(x_this);
#endif
m_aux[_h] = make_pair(_v.toBytes(), true);
}
void MemoryDB::purge()
{
#if DEV_GUARDED_DB
WriteGuard l(x_this);
#endif
for (auto it = m_main.begin(); it != m_main.end(); )
if (it->second.second)
++it;
@ -129,7 +170,9 @@ void MemoryDB::purge()
h256Hash MemoryDB::keys() const
{
#if DEV_GUARDED_DB
ReadGuard l(x_this);
#endif
h256Hash ret;
for (auto const& i: m_main)
if (i.second.second)

8
libdevcore/MemoryDB.h

@ -57,14 +57,16 @@ public:
bool kill(h256 const& _h);
void purge();
bytes lookupAux(h256 const& _h) const { ReadGuard l(x_this); auto it = m_aux.find(_h); if (it != m_aux.end() && (!m_enforceRefs || it->second.second)) return it->second.first; return bytes(); }
void removeAux(h256 const& _h) { WriteGuard l(x_this); m_aux[_h].second = false; }
void insertAux(h256 const& _h, bytesConstRef _v) { WriteGuard l(x_this); m_aux[_h] = make_pair(_v.toBytes(), true); }
bytes lookupAux(h256 const& _h) const;
void removeAux(h256 const& _h);
void insertAux(h256 const& _h, bytesConstRef _v);
h256Hash keys() const;
protected:
#if DEV_GUARDED_DB
mutable SharedMutex x_this;
#endif
std::unordered_map<h256, std::pair<std::string, unsigned>> m_main;
std::unordered_map<h256, std::pair<bytes, bool>> m_aux;

8
libdevcore/RangeMask.h

@ -219,6 +219,14 @@ public:
return uit == m_ranges.end() ? m_all.second : uit->first;
}
size_t size() const
{
size_t c = 0;
for (auto const& r: this->m_ranges)
c += r.second - r.first;
return c;
}
private:
UnsignedRange m_all;
std::map<T, T> m_ranges;

3
libdevcore/vector_ref.h

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

4
libdevcrypto/Common.cpp

@ -54,7 +54,7 @@ Public dev::toPublic(Secret const& _secret)
{
Public p;
s_secp256k1.toPublic(_secret, p);
return std::move(p);
return p;
}
Address dev::toAddress(Public const& _public)
@ -230,7 +230,7 @@ h256 crypto::kdf(Secret const& _priv, h256 const& _hash)
if (!s || !_hash || !_priv)
BOOST_THROW_EXCEPTION(InvalidState());
return std::move(s);
return s;
}
h256 Nonce::get(bool _commit)

4
libdevcrypto/CryptoPP.cpp

@ -61,7 +61,7 @@ bytes Secp256k1::eciesKDF(Secret _z, bytes _s1, unsigned kdByteLen)
}
k.resize(kdByteLen);
return move(k);
return k;
}
void Secp256k1::encryptECIES(Public const& _k, bytes& io_cipher)
@ -264,7 +264,6 @@ Public Secp256k1::recover(Signature _signature, bytesConstRef _message)
ECP::Element x;
{
Guard l(x_curve);
m_curve.DecodePoint(x, encodedpoint, 33);
if (!m_curve.VerifyPoint(x))
return recovered;
@ -286,7 +285,6 @@ Public Secp256k1::recover(Signature _signature, bytesConstRef _message)
ECP::Point p;
byte recoveredbytes[65];
{
Guard l(x_curve);
// todo: make generator member
p = m_curve.CascadeMultiply(u2, x, u1, m_params.GetSubgroupGenerator());
m_curve.EncodePoint(recoveredbytes, p, false);

2
libdevcrypto/CryptoPP.h

@ -59,7 +59,7 @@ namespace crypto
using namespace CryptoPP;
inline ECP::Point publicToPoint(Public const& _p) { Integer x(_p.data(), 32); Integer y(_p.data() + 32, 32); return std::move(ECP::Point(x,y)); }
inline ECP::Point publicToPoint(Public const& _p) { Integer x(_p.data(), 32); Integer y(_p.data() + 32, 32); return ECP::Point(x,y); }
inline Integer secretToExponent(Secret const& _s) { return std::move(Integer(_s.data(), Secret::size)); }

10
libdevcrypto/OverlayDB.cpp

@ -50,7 +50,9 @@ void OverlayDB::commit()
{
ldb::WriteBatch batch;
// cnote << "Committing nodes to disk DB:";
#if DEV_GUARDED_DB
DEV_READ_GUARDED(x_this)
#endif
{
for (auto const& i: m_main)
{
@ -83,7 +85,9 @@ void OverlayDB::commit()
cwarn << "Sleeping for" << (i + 1) << "seconds, then retrying.";
this_thread::sleep_for(chrono::seconds(i + 1));
}
#if DEV_GUARDED_DB
DEV_WRITE_GUARDED(x_this)
#endif
{
m_aux.clear();
m_main.clear();
@ -95,7 +99,7 @@ bytes OverlayDB::lookupAux(h256 const& _h) const
{
bytes ret = MemoryDB::lookupAux(_h);
if (!ret.empty() || !m_db)
return move(ret);
return ret;
std::string v;
bytes b = _h.asBytes();
b.push_back(255); // for aux
@ -107,7 +111,9 @@ bytes OverlayDB::lookupAux(h256 const& _h) const
void OverlayDB::rollback()
{
#if DEV_GUARDED_DB
WriteGuard l(x_this);
#endif
m_main.clear();
}
@ -116,7 +122,7 @@ std::string OverlayDB::lookup(h256 const& _h) const
std::string ret = MemoryDB::lookup(_h);
if (ret.empty() && m_db)
m_db->Get(m_readOptions, ldb::Slice((char const*)_h.data(), 32), &ret);
return move(ret);
return ret;
}
bool OverlayDB::exists(h256 const& _h) const

203
libethash-cl/ethash_cl_miner.cpp

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

17
libethash-cl/ethash_cl_miner.h

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

24
libethcore/BlockInfo.cpp

@ -139,7 +139,6 @@ void BlockInfo::populateFromHeader(RLP const& _header, Strictness _s, h256 const
mixHash = _header[field = 13].toHash<h256>(RLP::VeryStrict);
nonce = _header[field = 14].toHash<Nonce>(RLP::VeryStrict);
}
catch (Exception const& _e)
{
_e << errinfo_name("invalid block header format") << BadFieldError(field, toHex(_header[field].data().toBytes()));
@ -151,9 +150,26 @@ void BlockInfo::populateFromHeader(RLP const& _header, Strictness _s, h256 const
// check it hashes according to proof of work or that it's the genesis block.
if (_s == CheckEverything && parentHash && !ProofOfWork::verify(*this))
BOOST_THROW_EXCEPTION(InvalidBlockNonce() << errinfo_hash256(headerHash(WithoutNonce)) << errinfo_nonce(nonce) << errinfo_difficulty(difficulty));
{
InvalidBlockNonce ex;
ex << errinfo_hash256(headerHash(WithoutNonce));
ex << errinfo_nonce(nonce);
ex << errinfo_difficulty(difficulty);
ex << errinfo_seedHash(seedHash());
ex << errinfo_target(boundary());
ex << errinfo_mixHash(mixHash);
Ethash::Result er = EthashAux::eval(seedHash(), headerHash(WithoutNonce), nonce);
ex << errinfo_ethashResult(make_tuple(er.value, er.mixHash));
BOOST_THROW_EXCEPTION(ex);
}
else if (_s == QuickNonce && parentHash && !ProofOfWork::preVerify(*this))
BOOST_THROW_EXCEPTION(InvalidBlockNonce() << errinfo_hash256(headerHash(WithoutNonce)) << errinfo_nonce(nonce) << errinfo_difficulty(difficulty));
{
InvalidBlockNonce ex;
ex << errinfo_hash256(headerHash(WithoutNonce));
ex << errinfo_nonce(nonce);
ex << errinfo_difficulty(difficulty);
BOOST_THROW_EXCEPTION(ex);
}
if (_s != CheckNothing)
{
@ -224,7 +240,7 @@ void BlockInfo::verifyInternals(bytesConstRef _block) const
for (auto const& t: txs)
cdebug << toHex(t);
BOOST_THROW_EXCEPTION(InvalidTransactionsHash() << HashMismatchError(expectedRoot, transactionsRoot));
BOOST_THROW_EXCEPTION(InvalidTransactionsRoot() << Hash256RequirementError(expectedRoot, transactionsRoot));
}
clog(BlockInfoDiagnosticsChannel) << "Expected uncle hash:" << toString(sha3(root[2].data()));
if (sha3Uncles != sha3(root[2].data()))

29
libethcore/Common.cpp

@ -112,25 +112,26 @@ std::string formatBalance(bigint const& _b)
static void badBlockInfo(BlockInfo const& _bi, string const& _err)
{
cwarn << EthRedBold << "========================================================================";
cwarn << EthRedBold << "== Software Failure " + _err + string(max<int>(0, 44 - _err.size()), ' ') + " ==";
string const c_line = EthReset EthOnMaroon + string(80, ' ');
string const c_border = EthReset EthOnMaroon + string(2, ' ') + EthReset EthMaroonBold;
string const c_space = c_border + string(76, ' ') + c_border;
stringstream ss;
ss << c_line << endl;
ss << c_space << endl;
ss << c_border + " Import Failure " + _err + string(max<int>(0, 53 - _err.size()), ' ') + " " + c_border << endl;
ss << c_space << endl;
string bin = toString(_bi.number);
cwarn << EthRedBold << ("== Guru Meditation #" + string(max<int>(0, 8 - bin.size()), '0') + bin + "." + _bi.hash().abridged() + " ==");
cwarn << EthRedBold << "========================================================================";
ss << c_border + (" Guru Meditation #" + string(max<int>(0, 8 - bin.size()), '0') + bin + "." + _bi.hash().abridged() + " ") + c_border << endl;
ss << c_space << endl;
ss << c_line;
cwarn << "\n" + ss.str();
}
void badBlock(bytesConstRef _block, string const& _err)
{
badBlockInfo(BlockInfo(_block, CheckNothing), _err);
cwarn << " Block:" << toHex(_block);
cwarn << " Block RLP:" << RLP(_block);
}
void badBlockHeader(bytesConstRef _header, string const& _err)
{
badBlockInfo(BlockInfo::fromHeader(_header, CheckNothing), _err);
cwarn << " Header:" << toHex(_header);
cwarn << " Header RLP:" << RLP(_header);;
BlockInfo bi;
DEV_IGNORE_EXCEPTIONS(bi = BlockInfo(_block, CheckNothing));
badBlockInfo(bi, _err);
}
}

6
libethcore/Common.h

@ -85,6 +85,10 @@ using BlockNumber = unsigned;
static const BlockNumber LatestBlock = (BlockNumber)-2;
static const BlockNumber PendingBlock = (BlockNumber)-1;
static const h256 LatestBlockHash = h256(2);
static const h256 EarliestBlockHash = h256(1);
static const h256 PendingBlockHash = h256(0);
enum class RelativeBlock: BlockNumber
{
@ -156,8 +160,6 @@ struct TransactionSkeleton
u256 gasPrice = UndefinedU256;
};
void badBlockHeader(bytesConstRef _header, std::string const& _err);
inline void badBlockHeader(bytes const& _header, std::string const& _err) { badBlockHeader(&_header, _err); }
void badBlock(bytesConstRef _header, std::string const& _err);
inline void badBlock(bytes const& _header, std::string const& _err) { badBlock(&_header, _err); }

25
libethcore/Ethash.cpp

@ -285,7 +285,6 @@ private:
unsigned Ethash::GPUMiner::s_platformId = 0;
unsigned Ethash::GPUMiner::s_deviceId = 0;
unsigned Ethash::GPUMiner::s_numInstances = 0;
unsigned Ethash::GPUMiner::s_dagChunks = 1;
Ethash::GPUMiner::GPUMiner(ConstructionInfo const& _ci):
Miner(_ci),
@ -335,18 +334,19 @@ void Ethash::GPUMiner::workLoop()
EthashAux::FullType dag;
while (true)
{
if ((dag = EthashAux::full(w.seedHash, false)))
if ((dag = EthashAux::full(w.seedHash, true)))
break;
if (shouldStop())
{
delete m_miner;
m_miner = nullptr;
return;
}
cnote << "Awaiting DAG";
this_thread::sleep_for(chrono::milliseconds(500));
}
bytesConstRef dagData = dag->data();
m_miner->init(dagData.data(), dagData.size(), 32, s_platformId, device, s_dagChunks);
m_miner->init(dagData.data(), dagData.size(), 32, s_platformId, device);
}
uint64_t upper64OfBoundary = (uint64_t)(u64)((u256)w.boundary >> 192);
@ -354,6 +354,8 @@ void Ethash::GPUMiner::workLoop()
}
catch (cl::Error const& _e)
{
delete m_miner;
m_miner = nullptr;
cwarn << "Error GPU mining: " << _e.what() << "(" << _e.err() << ")";
}
}
@ -364,11 +366,6 @@ 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);
@ -376,7 +373,17 @@ std::string Ethash::GPUMiner::platformInfo()
unsigned Ethash::GPUMiner::getNumDevices()
{
return ethash_cl_miner::get_num_devices(s_platformId);
return ethash_cl_miner::getNumDevices(s_platformId);
}
void Ethash::GPUMiner::listDevices()
{
return ethash_cl_miner::listDevices();
}
bool Ethash::GPUMiner::configureGPU()
{
return ethash_cl_miner::configureGPU();
}
#endif

8
libethcore/Ethash.h

@ -87,10 +87,11 @@ 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 setDagChunks(unsigned) {}
static void setDefaultDevice(unsigned) {}
static void listDevices() {}
static bool configureGPU() { return false; }
static void setNumInstances(unsigned _instances) { s_numInstances = std::min<unsigned>(_instances, std::thread::hardware_concurrency()); }
protected:
void kickOff() override
@ -117,12 +118,12 @@ public:
static unsigned instances() { return s_numInstances > 0 ? s_numInstances : 1; }
static std::string platformInfo();
static bool haveSufficientGPUMemory();
static unsigned getNumDevices();
static void listDevices();
static bool configureGPU();
static void setDefaultPlatform(unsigned _id) { s_platformId = _id; }
static void setDefaultDevice(unsigned _id) { s_deviceId = _id; }
static void setNumInstances(unsigned _instances) { s_numInstances = std::min<unsigned>(_instances, getNumDevices()); }
static void setDagChunks(unsigned _dagChunks) { s_dagChunks = _dagChunks; }
protected:
void kickOff() override;
@ -141,7 +142,6 @@ public:
static unsigned s_platformId;
static unsigned s_deviceId;
static unsigned s_numInstances;
static unsigned s_dagChunks;
};
#else
using GPUMiner = CPUMiner;

77
libethcore/Exceptions.h

@ -35,46 +35,45 @@ using errinfo_field = boost::error_info<struct tag_field, int>;
using errinfo_data = boost::error_info<struct tag_data, std::string>;
using errinfo_nonce = boost::error_info<struct tag_nonce, h64>;
using errinfo_difficulty = boost::error_info<struct tag_difficulty, u256>;
using errinfo_target = boost::error_info<struct tag_target, h256>;
using errinfo_seedHash = boost::error_info<struct tag_seedHash, h256>;
using errinfo_mixHash = boost::error_info<struct tag_mixHash, h256>;
using errinfo_ethashResult = boost::error_info<struct tag_ethashResult, std::tuple<h256, h256>>;
using BadFieldError = boost::tuple<errinfo_field, errinfo_data>;
struct DatabaseAlreadyOpen: virtual dev::Exception {};
struct OutOfGasBase: virtual dev::Exception {};
struct NotEnoughAvailableSpace: virtual dev::Exception {};
struct NotEnoughCash: virtual dev::Exception {};
struct GasPriceTooLow: virtual dev::Exception {};
struct BlockGasLimitReached: virtual dev::Exception {};
struct NoSuchContract: virtual dev::Exception {};
struct ContractAddressCollision: virtual dev::Exception {};
struct FeeTooSmall: virtual dev::Exception {};
struct TooMuchGasUsed: virtual dev::Exception {};
struct ExtraDataTooBig: virtual dev::Exception {};
struct InvalidSignature: virtual 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 {};
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 {};
struct InvalidTransactionsHash: virtual dev::Exception {};
struct InvalidTransaction: virtual dev::Exception {};
struct InvalidDifficulty: virtual 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 {};
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 {};
struct DAGCreationFailure: virtual public dev::Exception {};
struct DAGComputeFailure: virtual public dev::Exception {};
DEV_SIMPLE_EXCEPTION(OutOfGasBase);
DEV_SIMPLE_EXCEPTION(OutOfGasIntrinsic);
DEV_SIMPLE_EXCEPTION(NotEnoughAvailableSpace);
DEV_SIMPLE_EXCEPTION(NotEnoughCash);
DEV_SIMPLE_EXCEPTION(GasPriceTooLow);
DEV_SIMPLE_EXCEPTION(BlockGasLimitReached);
DEV_SIMPLE_EXCEPTION(FeeTooSmall);
DEV_SIMPLE_EXCEPTION(TooMuchGasUsed);
DEV_SIMPLE_EXCEPTION(ExtraDataTooBig);
DEV_SIMPLE_EXCEPTION(InvalidSignature);
DEV_SIMPLE_EXCEPTION(InvalidBlockFormat);
DEV_SIMPLE_EXCEPTION(InvalidUnclesHash);
DEV_SIMPLE_EXCEPTION(TooManyUncles);
DEV_SIMPLE_EXCEPTION(UncleTooOld);
DEV_SIMPLE_EXCEPTION(UncleIsBrother);
DEV_SIMPLE_EXCEPTION(UncleInChain);
DEV_SIMPLE_EXCEPTION(InvalidStateRoot);
DEV_SIMPLE_EXCEPTION(InvalidGasUsed);
DEV_SIMPLE_EXCEPTION(InvalidTransactionsRoot);
DEV_SIMPLE_EXCEPTION(InvalidDifficulty);
DEV_SIMPLE_EXCEPTION(InvalidGasLimit);
DEV_SIMPLE_EXCEPTION(InvalidReceiptsStateRoot);
DEV_SIMPLE_EXCEPTION(InvalidTimestamp);
DEV_SIMPLE_EXCEPTION(InvalidLogBloom);
DEV_SIMPLE_EXCEPTION(InvalidNonce);
DEV_SIMPLE_EXCEPTION(InvalidBlockHeaderItemCount);
DEV_SIMPLE_EXCEPTION(InvalidBlockNonce);
DEV_SIMPLE_EXCEPTION(InvalidParentHash);
DEV_SIMPLE_EXCEPTION(InvalidNumber);
DEV_SIMPLE_EXCEPTION(DatabaseAlreadyOpen);
DEV_SIMPLE_EXCEPTION(DAGCreationFailure);
DEV_SIMPLE_EXCEPTION(DAGComputeFailure);
}
}

267
libethereum/BlockChain.cpp

@ -35,6 +35,7 @@
#include <libdevcore/StructuredLogger.h>
#include <libdevcore/FileSystem.h>
#include <libethcore/Exceptions.h>
#include <libethcore/EthashAux.h>
#include <libethcore/ProofOfWork.h>
#include <libethcore/BlockInfo.h>
#include <libethcore/Params.h>
@ -42,13 +43,14 @@
#include "GenesisInfo.h"
#include "State.h"
#include "Defaults.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
namespace js = json_spirit;
#define ETH_CATCH 1
#define ETH_TIMED_IMPORTS 0
#define ETH_TIMED_IMPORTS 1
#ifdef _WIN32
const char* BlockChainDebug::name() { return EthBlue "8" EthWhite " <>"; }
@ -307,38 +309,50 @@ tuple<h256s, h256s, bool> BlockChain::sync(BlockQueue& _bq, OverlayDB const& _st
{
// _bq.tick(*this);
vector<pair<BlockInfo, bytes>> blocks;
VerifiedBlocks blocks;
_bq.drain(blocks, _max);
h256s fresh;
h256s dead;
h256s badBlocks;
for (auto const& block: blocks)
{
try
{
// Nonce & uncle nonces already verified in verification thread at this point.
ImportRoute r;
DEV_TIMED_ABOVE(Block import, 500)
r = import(block.first, block.second, _stateDB, ImportRequirements::Default & ~ImportRequirements::ValidNonce & ~ImportRequirements::CheckUncles);
fresh += r.first;
dead += r.second;
}
catch (dev::eth::UnknownParent)
{
cwarn << "ODD: Import queue contains block with unknown parent." << LogTag::Error << boost::current_exception_diagnostic_information();
// NOTE: don't reimport since the queue should guarantee everything in the right order.
// Can't continue - chain bad.
badBlocks.push_back(block.first.hash());
}
catch (Exception const& _e)
for (VerifiedBlock const& block: blocks)
if (!badBlocks.empty())
badBlocks.push_back(block.verified.info.hash());
else
{
cnote << "Exception while importing block. Someone (Jeff? That you?) seems to be giving us dodgy blocks!" << LogTag::Error << diagnostic_information(_e);
// NOTE: don't reimport since the queue should guarantee everything in the right order.
// Can't continue - chain bad.
badBlocks.push_back(block.first.hash());
try
{
// Nonce & uncle nonces already verified in verification thread at this point.
ImportRoute r;
DEV_TIMED_ABOVE(Block import, 500)
r = import(block.verified, _stateDB, ImportRequirements::Default & ~ImportRequirements::ValidNonce & ~ImportRequirements::CheckUncles);
fresh += r.first;
dead += r.second;
}
catch (dev::eth::UnknownParent)
{
cwarn << "ODD: Import queue contains block with unknown parent.";// << LogTag::Error << boost::current_exception_diagnostic_information();
// NOTE: don't reimport since the queue should guarantee everything in the right order.
// Can't continue - chain bad.
badBlocks.push_back(block.verified.info.hash());
}
catch (dev::eth::FutureTime)
{
cwarn << "ODD: Import queue contains a block with future time.";// << LogTag::Error << boost::current_exception_diagnostic_information();
// NOTE: don't reimport since the queue should guarantee everything in the past.
// Can't continue - chain bad.
badBlocks.push_back(block.verified.info.hash());
}
catch (Exception& ex)
{
// cnote << "Exception while importing block. Someone (Jeff? That you?) seems to be giving us dodgy blocks!";// << LogTag::Error << diagnostic_information(ex);
if (m_onBad)
m_onBad(ex);
// NOTE: don't reimport since the queue should guarantee everything in the right order.
// Can't continue - chain bad.
badBlocks.push_back(block.verified.info.hash());
}
}
}
return make_tuple(fresh, dead, _bq.doneDrain(badBlocks));
}
@ -346,7 +360,7 @@ pair<ImportResult, ImportRoute> BlockChain::attemptImport(bytes const& _block, O
{
try
{
return make_pair(ImportResult::Success, import(_block, _stateDB, _ir));
return make_pair(ImportResult::Success, import(verifyBlock(_block, m_onBad), _stateDB, _ir));
}
catch (UnknownParent&)
{
@ -360,8 +374,10 @@ pair<ImportResult, ImportRoute> BlockChain::attemptImport(bytes const& _block, O
{
return make_pair(ImportResult::FutureTime, make_pair(h256s(), h256s()));
}
catch (...)
catch (Exception& ex)
{
if (m_onBad)
m_onBad(ex);
return make_pair(ImportResult::Malformed, make_pair(h256s(), h256s()));
}
}
@ -369,28 +385,28 @@ pair<ImportResult, ImportRoute> BlockChain::attemptImport(bytes const& _block, O
ImportRoute BlockChain::import(bytes const& _block, OverlayDB const& _db, ImportRequirements::value _ir)
{
// VERIFY: populates from the block and checks the block is internally coherent.
BlockInfo bi;
VerifiedBlockRef block;
#if ETH_CATCH
try
#endif
{
bi.populate(&_block);
bi.verifyInternals(&_block);
block = verifyBlock(_block, m_onBad);
}
#if ETH_CATCH
catch (Exception const& _e)
catch (Exception& ex)
{
clog(BlockChainNote) << " Malformed block: " << diagnostic_information(_e);
_e << errinfo_comment("Malformed block ");
// clog(BlockChainNote) << " Malformed block: " << diagnostic_information(ex);
ex << errinfo_now(time(0));
ex << errinfo_block(_block);
throw;
}
#endif
return import(bi, _block, _db, _ir);
return import(block, _db, _ir);
}
ImportRoute BlockChain::import(BlockInfo const& _bi, bytes const& _block, OverlayDB const& _db, ImportRequirements::value _ir)
ImportRoute BlockChain::import(VerifiedBlockRef const& _block, OverlayDB const& _db, ImportRequirements::value _ir)
{
//@tidy This is a behemoth of a method - could do to be split into a few smaller ones.
@ -405,28 +421,28 @@ ImportRoute BlockChain::import(BlockInfo const& _bi, bytes const& _block, Overla
#endif
// Check block doesn't already exist first!
if (isKnown(_bi.hash()) && (_ir & ImportRequirements::DontHave))
if (isKnown(_block.info.hash()) && (_ir & ImportRequirements::DontHave))
{
clog(BlockChainNote) << _bi.hash() << ": Not new.";
clog(BlockChainNote) << _block.info.hash() << ": Not new.";
BOOST_THROW_EXCEPTION(AlreadyHaveBlock());
}
// Work out its number as the parent's number + 1
if (!isKnown(_bi.parentHash))
if (!isKnown(_block.info.parentHash))
{
clog(BlockChainNote) << _bi.hash() << ": Unknown parent " << _bi.parentHash;
clog(BlockChainNote) << _block.info.hash() << ": Unknown parent " << _block.info.parentHash;
// We don't know the parent (yet) - discard for now. It'll get resent to us if we find out about its ancestry later on.
BOOST_THROW_EXCEPTION(UnknownParent());
}
auto pd = details(_bi.parentHash);
auto pd = details(_block.info.parentHash);
if (!pd)
{
auto pdata = pd.rlp();
clog(BlockChainDebug) << "Details is returning false despite block known:" << RLP(pdata);
auto parentBlock = block(_bi.parentHash);
clog(BlockChainDebug) << "isKnown:" << isKnown(_bi.parentHash);
clog(BlockChainDebug) << "last/number:" << m_lastBlockNumber << m_lastBlockHash << _bi.number;
auto parentBlock = block(_block.info.parentHash);
clog(BlockChainDebug) << "isKnown:" << isKnown(_block.info.parentHash);
clog(BlockChainDebug) << "last/number:" << m_lastBlockNumber << m_lastBlockHash << _block.info.number;
clog(BlockChainDebug) << "Block:" << BlockInfo(parentBlock);
clog(BlockChainDebug) << "RLP:" << RLP(parentBlock);
clog(BlockChainDebug) << "DATABASE CORRUPTION: CRITICAL FAILURE";
@ -434,14 +450,14 @@ ImportRoute BlockChain::import(BlockInfo const& _bi, bytes const& _block, Overla
}
// Check it's not crazy
if (_bi.timestamp > (u256)time(0))
if (_block.info.timestamp > (u256)time(0))
{
clog(BlockChainChat) << _bi.hash() << ": Future time " << _bi.timestamp << " (now at " << time(0) << ")";
clog(BlockChainChat) << _block.info.hash() << ": Future time " << _block.info.timestamp << " (now at " << time(0) << ")";
// Block has a timestamp in the future. This is no good.
BOOST_THROW_EXCEPTION(FutureTime());
}
clog(BlockChainChat) << "Attempting import of " << _bi.hash() << "...";
clog(BlockChainChat) << "Attempting import of " << _block.info.hash() << "...";
#if ETH_TIMED_IMPORTS
preliminaryChecks = t.elapsed();
@ -461,7 +477,7 @@ ImportRoute BlockChain::import(BlockInfo const& _bi, bytes const& _block, Overla
// Check transactions are valid and that they result in a state equivalent to our state_root.
// Get total difficulty increase and update state, checking it.
State s(_db);
auto tdIncrease = s.enactOn(&_block, _bi, *this, _ir);
auto tdIncrease = s.enactOn(_block, *this, _ir);
BlockLogBlooms blb;
BlockReceipts br;
@ -470,14 +486,8 @@ ImportRoute BlockChain::import(BlockInfo const& _bi, bytes const& _block, Overla
blb.blooms.push_back(s.receipt(i).bloom());
br.receipts.push_back(s.receipt(i));
}
try {
s.cleanup(true);
}
catch (BadRoot)
{
cwarn << "BadRoot error. Retrying import later.";
BOOST_THROW_EXCEPTION(FutureTime());
}
s.cleanup(true);
td = pd.totalDifficulty + tdIncrease;
@ -497,22 +507,22 @@ ImportRoute BlockChain::import(BlockInfo const& _bi, bytes const& _block, Overla
// together with an "ensureCachedWithUpdatableLock(l)" method.
// This is safe in practice since the caches don't get flushed nearly often enough to be
// done here.
details(_bi.parentHash);
details(_block.info.parentHash);
DEV_WRITE_GUARDED(x_details)
m_details[_bi.parentHash].children.push_back(_bi.hash());
m_details[_block.info.parentHash].children.push_back(_block.info.hash());
#if ETH_TIMED_IMPORTS || !ETH_TRUE
collation = t.elapsed();
t.restart();
#endif
blocksBatch.Put(toSlice(_bi.hash()), (ldb::Slice)ref(_block));
blocksBatch.Put(toSlice(_block.info.hash()), ldb::Slice(_block.block));
DEV_READ_GUARDED(x_details)
extrasBatch.Put(toSlice(_bi.parentHash, ExtraDetails), (ldb::Slice)dev::ref(m_details[_bi.parentHash].rlp()));
extrasBatch.Put(toSlice(_block.info.parentHash, ExtraDetails), (ldb::Slice)dev::ref(m_details[_block.info.parentHash].rlp()));
extrasBatch.Put(toSlice(_bi.hash(), ExtraDetails), (ldb::Slice)dev::ref(BlockDetails((unsigned)pd.number + 1, td, _bi.parentHash, {}).rlp()));
extrasBatch.Put(toSlice(_bi.hash(), ExtraLogBlooms), (ldb::Slice)dev::ref(blb.rlp()));
extrasBatch.Put(toSlice(_bi.hash(), ExtraReceipts), (ldb::Slice)dev::ref(br.rlp()));
extrasBatch.Put(toSlice(_block.info.hash(), ExtraDetails), (ldb::Slice)dev::ref(BlockDetails((unsigned)pd.number + 1, td, _block.info.parentHash, {}).rlp()));
extrasBatch.Put(toSlice(_block.info.hash(), ExtraLogBlooms), (ldb::Slice)dev::ref(blb.rlp()));
extrasBatch.Put(toSlice(_block.info.hash(), ExtraReceipts), (ldb::Slice)dev::ref(br.rlp()));
#if ETH_TIMED_IMPORTS || !ETH_TRUE
writing = t.elapsed();
@ -520,30 +530,25 @@ ImportRoute BlockChain::import(BlockInfo const& _bi, bytes const& _block, Overla
#endif
}
#if ETH_CATCH
catch (InvalidNonce const& _e)
catch (BadRoot& ex)
{
clog(BlockChainNote) << " Malformed block: " << diagnostic_information(_e);
_e << errinfo_comment("Malformed block ");
throw;
cwarn << "BadRoot error. Retrying import later.";
BOOST_THROW_EXCEPTION(FutureTime());
}
catch (Exception const& _e)
catch (Exception& ex)
{
clog(BlockChainWarn) << " Malformed block: " << diagnostic_information(_e);
_e << errinfo_comment("Malformed block ");
clog(BlockChainWarn) << "Block: " << _bi.hash();
clog(BlockChainWarn) << _bi;
clog(BlockChainWarn) << "Block parent: " << _bi.parentHash;
clog(BlockChainWarn) << BlockInfo(block(_bi.parentHash));
ex << errinfo_now(time(0));
ex << errinfo_block(_block.block.toBytes());
throw;
}
#endif
StructuredLogger::chainReceivedNewBlock(
_bi.headerHash(WithoutNonce).abridged(),
_bi.nonce.abridged(),
_block.info.headerHash(WithoutNonce).abridged(),
_block.info.nonce.abridged(),
currentHash().abridged(),
"", // TODO: remote id ??
_bi.parentHash.abridged()
_block.info.parentHash.abridged()
);
// cnote << "Parent " << bi.parentHash << " has " << details(bi.parentHash).children.size() << " children.";
@ -556,8 +561,8 @@ ImportRoute BlockChain::import(BlockInfo const& _bi, bytes const& _block, Overla
// don't include bi.hash() in treeRoute, since it's not yet in details DB...
// just tack it on afterwards.
unsigned commonIndex;
tie(route, common, commonIndex) = treeRoute(last, _bi.parentHash);
route.push_back(_bi.hash());
tie(route, common, commonIndex) = treeRoute(last, _block.info.parentHash);
route.push_back(_block.info.hash());
// Most of the time these two will be equal - only when we're doing a chain revert will they not be
if (common != last)
@ -569,8 +574,8 @@ ImportRoute BlockChain::import(BlockInfo const& _bi, bytes const& _block, Overla
for (auto i = route.rbegin(); i != route.rend() && *i != common; ++i)
{
BlockInfo tbi;
if (*i == _bi.hash())
tbi = _bi;
if (*i == _block.info.hash())
tbi = _block.info;
else
tbi = BlockInfo(block(*i));
@ -597,7 +602,7 @@ ImportRoute BlockChain::import(BlockInfo const& _bi, bytes const& _block, Overla
h256s newTransactionAddresses;
{
bytes blockBytes;
RLP blockRLP(*i == _bi.hash() ? _block : (blockBytes = block(*i)));
RLP blockRLP(*i == _block.info.hash() ? _block.block : &(blockBytes = block(*i)));
TransactionAddress ta;
ta.blockHash = tbi.hash();
for (ta.index = 0; ta.index < blockRLP[1].itemCount(); ++ta.index)
@ -613,17 +618,17 @@ ImportRoute BlockChain::import(BlockInfo const& _bi, bytes const& _block, Overla
// FINALLY! change our best hash.
{
newLastBlockHash = _bi.hash();
newLastBlockNumber = (unsigned)_bi.number;
newLastBlockHash = _block.info.hash();
newLastBlockNumber = (unsigned)_block.info.number;
}
clog(BlockChainNote) << " Imported and best" << td << " (#" << _bi.number << "). Has" << (details(_bi.parentHash).children.size() - 1) << "siblings. Route:" << route;
clog(BlockChainNote) << " Imported and best" << td << " (#" << _block.info.number << "). Has" << (details(_block.info.parentHash).children.size() - 1) << "siblings. Route:" << route;
StructuredLogger::chainNewHead(
_bi.headerHash(WithoutNonce).abridged(),
_bi.nonce.abridged(),
_block.info.headerHash(WithoutNonce).abridged(),
_block.info.nonce.abridged(),
currentHash().abridged(),
_bi.parentHash.abridged()
_block.info.parentHash.abridged()
);
}
else
@ -634,24 +639,26 @@ ImportRoute BlockChain::import(BlockInfo const& _bi, bytes const& _block, Overla
m_blocksDB->Write(m_writeOptions, &blocksBatch);
m_extrasDB->Write(m_writeOptions, &extrasBatch);
if (isKnown(_bi.hash()) && !details(_bi.hash()))
#if ETH_PARANOIA || !ETH_TRUE
if (isKnown(_block.info.hash()) && !details(_block.info.hash()))
{
clog(BlockChainDebug) << "Known block just inserted has no details.";
clog(BlockChainDebug) << "Block:" << _bi;
clog(BlockChainDebug) << "Block:" << _block.info;
clog(BlockChainDebug) << "DATABASE CORRUPTION: CRITICAL FAILURE";
exit(-1);
}
try {
State canary(_db, *this, _bi.hash(), ImportRequirements::DontHave);
State canary(_db, *this, _block.info.hash(), ImportRequirements::DontHave);
}
catch (...)
{
clog(BlockChainDebug) << "Failed to initialise State object form imported block.";
clog(BlockChainDebug) << "Block:" << _bi;
clog(BlockChainDebug) << "Block:" << _block.info;
clog(BlockChainDebug) << "DATABASE CORRUPTION: CRITICAL FAILURE";
exit(-1);
}
#endif
if (m_lastBlockHash != newLastBlockHash)
DEV_WRITE_GUARDED(x_lastBlockHash)
@ -667,12 +674,16 @@ ImportRoute BlockChain::import(BlockInfo const& _bi, bytes const& _block, Overla
#if ETH_TIMED_IMPORTS
checkBest = t.elapsed();
cnote << "Import took:" << total.elapsed();
cnote << "preliminaryChecks:" << preliminaryChecks;
cnote << "enactment:" << enactment;
cnote << "collation:" << collation;
cnote << "writing:" << writing;
cnote << "checkBest:" << checkBest;
if (total.elapsed() > 1.0)
{
cnote << "SLOW IMPORT:" << _block.info.hash();
cnote << " Import took:" << total.elapsed();
cnote << " preliminaryChecks:" << preliminaryChecks;
cnote << " enactment:" << enactment;
cnote << " collation:" << collation;
cnote << " writing:" << writing;
cnote << " checkBest:" << checkBest;
}
#endif
if (!route.empty())
@ -915,8 +926,8 @@ void BlockChain::checkConsistency()
delete it;
}
static inline unsigned upow(unsigned a, unsigned b) { while (b-- > 0) a *= a; return a; }
static inline unsigned ceilDiv(unsigned n, unsigned d) { return n / (n + d - 1); }
static inline unsigned upow(unsigned a, unsigned b) { if (!b) return 1; while (--b > 0) a *= a; return a; }
static inline unsigned ceilDiv(unsigned n, unsigned d) { return (n + d - 1) / d; }
//static inline unsigned floorDivPow(unsigned n, unsigned a, unsigned b) { return n / upow(a, b); }
//static inline unsigned ceilDivPow(unsigned n, unsigned a, unsigned b) { return ceilDiv(n, upow(a, b)); }
@ -1054,3 +1065,59 @@ bytes BlockChain::block(h256 const& _hash) const
return m_blocks[_hash];
}
VerifiedBlockRef BlockChain::verifyBlock(bytes const& _block, function<void(Exception&)> const& _onBad)
{
VerifiedBlockRef res;
try
{
res.info.populate(_block, CheckEverything);
res.info.verifyInternals(&_block);
}
catch (Exception& ex)
{
ex << errinfo_now(time(0));
ex << errinfo_block(_block);
if (_onBad)
_onBad(ex);
throw;
}
RLP r(_block);
unsigned i = 0;
for (auto const& uncle: r[2])
{
try
{
BlockInfo().populateFromHeader(RLP(uncle.data()), CheckEverything);
}
catch (Exception& ex)
{
ex << errinfo_uncleIndex(i);
ex << errinfo_now(time(0));
ex << errinfo_block(_block);
if (_onBad)
_onBad(ex);
throw;
}
++i;
}
i = 0;
for (auto const& tr: r[1])
{
try
{
res.transactions.push_back(Transaction(tr.data(), CheckTransaction::Everything));
}
catch (Exception& ex)
{
ex << errinfo_transactionIndex(i);
ex << errinfo_block(_block);
throw;
}
++i;
}
res.block = bytesConstRef(&_block);
return move(res);
}

12
libethereum/BlockChain.h

@ -40,6 +40,7 @@
#include "Account.h"
#include "Transaction.h"
#include "BlockQueue.h"
#include "VerifiedBlock.h"
namespace ldb = leveldb;
namespace std
@ -120,7 +121,7 @@ public:
/// Import block into disk-backed DB
/// @returns the block hashes of any blocks that came into/went out of the canonical block chain.
ImportRoute import(bytes const& _block, OverlayDB const& _stateDB, ImportRequirements::value _ir = ImportRequirements::Default);
ImportRoute import(BlockInfo const& _bi, bytes const& _block, OverlayDB const& _stateDB, ImportRequirements::value _ir = ImportRequirements::Default);
ImportRoute import(VerifiedBlockRef const& _block, OverlayDB const& _db, ImportRequirements::value _ir = ImportRequirements::Default);
/// Returns true if the given block is known (though not necessarily a part of the canon chain).
bool isKnown(h256 const& _hash) const;
@ -143,6 +144,7 @@ public:
BlockLogBlooms logBlooms() const { return logBlooms(currentHash()); }
/// Get the transactions' receipts of a block (or the most recent mined if none given). Thread-safe.
/// receipts are given in the same order are in the same order as the transactions
BlockReceipts receipts(h256 const& _hash) const { return queryExtras<BlockReceipts, ExtraReceipts>(_hash, m_receipts, x_receipts, NullBlockReceipts); }
BlockReceipts receipts() const { return receipts(currentHash()); }
@ -256,6 +258,12 @@ public:
/// Deallocate unused data.
void garbageCollect(bool _force = false);
/// Verify block and prepare it for enactment
static VerifiedBlockRef verifyBlock(bytes const& _block, std::function<void(Exception&)> const& _onBad = std::function<void(Exception&)>());
/// Change the function that is called with a bad block.
template <class T> void setOnBad(T const& _t) { m_onBad = _t; }
private:
static h256 chunkId(unsigned _level, unsigned _index) { return h256(_index * 0xff + _level); }
@ -335,6 +343,8 @@ private:
ldb::ReadOptions m_readOptions;
ldb::WriteOptions m_writeOptions;
std::function<void(Exception&)> m_onBad; ///< Called if we have a block that doesn't verify.
friend std::ostream& operator<<(std::ostream& _out, BlockChain const& _bc);
};

274
libethereum/BlockQueue.cpp

@ -22,10 +22,11 @@
#include "BlockQueue.h"
#include <thread>
#include <libdevcore/Log.h>
#include <libethcore/EthashAux.h>
#include <libethcore/Exceptions.h>
#include <libethcore/BlockInfo.h>
#include "BlockChain.h"
#include "VerifiedBlock.h"
#include "State.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
@ -36,8 +37,16 @@ const char* BlockQueueChannel::name() { return EthOrange "[]>"; }
const char* BlockQueueChannel::name() { return EthOrange "▣┅▶"; }
#endif
size_t const c_maxKnownCount = 100000;
size_t const c_maxKnownSize = 128 * 1024 * 1024;
size_t const c_maxUnknownCount = 100000;
size_t const c_maxUnknownSize = 512 * 1024 * 1024; // Block size can be ~50kb
BlockQueue::BlockQueue()
BlockQueue::BlockQueue():
m_unknownSize(0),
m_knownSize(0),
m_unknownCount(0),
m_knownCount(0)
{
// Allow some room for other activity
unsigned verifierThreads = std::max(thread::hardware_concurrency(), 3U) - 2U;
@ -56,11 +65,29 @@ BlockQueue::~BlockQueue()
i.join();
}
void BlockQueue::clear()
{
WriteGuard l(m_lock);
DEV_INVARIANT_CHECK;
Guard l2(m_verification);
m_readySet.clear();
m_drainingSet.clear();
m_verified.clear();
m_unverified.clear();
m_unknownSet.clear();
m_unknown.clear();
m_future.clear();
m_unknownSize = 0;
m_unknownCount = 0;
m_knownSize = 0;
m_knownCount = 0;
}
void BlockQueue::verifierBody()
{
while (!m_deleting)
{
std::pair<h256, bytes> work;
UnverifiedBlock work;
{
unique_lock<Mutex> l(m_verification);
@ -70,63 +97,16 @@ void BlockQueue::verifierBody()
swap(work, m_unverified.front());
m_unverified.pop_front();
BlockInfo bi;
bi.mixHash = work.first;
m_verifying.push_back(make_pair(bi, bytes()));
bi.mixHash = work.hash;
bi.parentHash = work.parentHash;
m_verifying.push_back(VerifiedBlock { VerifiedBlockRef { bytesConstRef(), move(bi), Transactions() }, bytes() });
}
std::pair<BlockInfo, bytes> res;
swap(work.second, res.second);
try {
try {
res.first.populate(res.second, CheckEverything, work.first);
res.first.verifyInternals(&res.second);
}
catch (InvalidBlockNonce&)
{
badBlock(res.second, "Invalid block nonce");
cwarn << " Nonce:" << res.first.nonce.hex();
cwarn << " PoWHash:" << res.first.headerHash(WithoutNonce).hex();
cwarn << " SeedHash:" << res.first.seedHash().hex();
cwarn << " Target:" << res.first.boundary().hex();
cwarn << " MixHash:" << res.first.mixHash.hex();
Ethash::Result er = EthashAux::eval(res.first.seedHash(), res.first.headerHash(WithoutNonce), res.first.nonce);
cwarn << " Ethash v:" << er.value.hex();
cwarn << " Ethash mH:" << er.mixHash.hex();
throw;
}
catch (Exception& _e)
{
badBlock(res.second, _e.what());
throw;
}
RLP r(&res.second);
for (auto const& uncle: r[2])
{
try
{
BlockInfo().populateFromHeader(RLP(uncle.data()), CheckEverything);
}
catch (InvalidNonce&)
{
badBlockHeader(uncle.data(), "Invalid uncle nonce");
BlockInfo bi = BlockInfo::fromHeader(uncle.data(), CheckNothing);
cwarn << " Nonce:" << bi.nonce.hex();
cwarn << " PoWHash:" << bi.headerHash(WithoutNonce).hex();
cwarn << " SeedHash:" << bi.seedHash().hex();
cwarn << " Target:" << bi.boundary().hex();
cwarn << " MixHash:" << bi.mixHash.hex();
Ethash::Result er = EthashAux::eval(bi.seedHash(), bi.headerHash(WithoutNonce), bi.nonce);
cwarn << " Ethash v:" << er.value.hex();
cwarn << " Ethash mH:" << er.mixHash.hex();
throw;
}
catch (Exception& _e)
{
badBlockHeader(uncle.data(), _e.what());
throw;
}
}
VerifiedBlock res;
swap(work.block, res.blockData);
try
{
res.verified = BlockChain::verifyBlock(res.blockData, m_onBad);
}
catch (...)
{
@ -135,33 +115,46 @@ void BlockQueue::verifierBody()
// has to be this order as that's how invariants() assumes.
WriteGuard l2(m_lock);
unique_lock<Mutex> l(m_verification);
m_readySet.erase(work.first);
m_knownBad.insert(work.first);
m_readySet.erase(work.hash);
m_knownBad.insert(work.hash);
}
unique_lock<Mutex> l(m_verification);
for (auto it = m_verifying.begin(); it != m_verifying.end(); ++it)
if (it->first.mixHash == work.first)
if (it->verified.info.mixHash == work.hash)
{
m_verifying.erase(it);
goto OK1;
}
cwarn << "GAA BlockQueue corrupt: job cancelled but cannot be found in m_verifying queue.";
cwarn << "BlockQueue missing our job: was there a GM?";
OK1:;
continue;
}
bool ready = false;
{
WriteGuard l2(m_lock);
unique_lock<Mutex> l(m_verification);
if (m_verifying.front().first.mixHash == work.first)
if (!m_verifying.empty() && m_verifying.front().verified.info.mixHash == work.hash)
{
// we're next!
m_verifying.pop_front();
m_verified.push_back(move(res));
while (m_verifying.size() && !m_verifying.front().second.empty())
if (m_knownBad.count(res.verified.info.parentHash))
{
m_readySet.erase(res.verified.info.hash());
m_knownBad.insert(res.verified.info.hash());
}
else
m_verified.push_back(move(res));
while (m_verifying.size() && !m_verifying.front().blockData.empty())
{
m_verified.push_back(move(m_verifying.front()));
if (m_knownBad.count(m_verifying.front().verified.info.parentHash))
{
m_readySet.erase(m_verifying.front().verified.info.hash());
m_knownBad.insert(res.verified.info.hash());
}
else
m_verified.push_back(move(m_verifying.front()));
m_verifying.pop_front();
}
ready = true;
@ -169,12 +162,12 @@ void BlockQueue::verifierBody()
else
{
for (auto& i: m_verifying)
if (i.first.mixHash == work.first)
if (i.verified.info.mixHash == work.hash)
{
i = move(res);
goto OK;
}
cwarn << "GAA BlockQueue corrupt: job finished but cannot be found in m_verifying queue.";
cwarn << "BlockQueue missing our job: was there a GM?";
OK:;
}
}
@ -234,6 +227,8 @@ ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc, boo
if (strftime(buf, 24, "%X", localtime(&bit)) == 0)
buf[0] = '\0'; // empty if case strftime fails
cblockq << "OK - queued for future [" << bi.timestamp << "vs" << time(0) << "] - will wait until" << buf;
m_unknownSize += _block.size();
m_unknownCount++;
return ImportResult::FutureTime;
}
else
@ -242,6 +237,7 @@ ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc, boo
if (m_knownBad.count(bi.parentHash))
{
m_knownBad.insert(bi.hash());
updateBad(bi.hash());
// bad parent; this is bad too, note it as such
return ImportResult::BadChain;
}
@ -251,6 +247,8 @@ ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc, boo
cblockq << "OK - queued as unknown parent:" << bi.parentHash;
m_unknown.insert(make_pair(bi.parentHash, make_pair(h, _block.toBytes())));
m_unknownSet.insert(h);
m_unknownSize += _block.size();
m_unknownCount++;
return ImportResult::UnknownParent;
}
@ -259,9 +257,11 @@ ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc, boo
// If valid, append to blocks.
cblockq << "OK - ready for chain insertion.";
DEV_GUARDED(m_verification)
m_unverified.push_back(make_pair(h, _block.toBytes()));
m_unverified.push_back(UnverifiedBlock { h, bi.parentHash, _block.toBytes() });
m_moreToVerify.notify_one();
m_readySet.insert(h);
m_knownSize += _block.size();
m_knownCount++;
noteReady_WITH_LOCK(h);
@ -270,30 +270,93 @@ ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc, boo
}
}
bool BlockQueue::doneDrain(h256s const& _bad)
void BlockQueue::updateBad(h256 const& _bad)
{
WriteGuard l(m_lock);
DEV_INVARIANT_CHECK;
m_drainingSet.clear();
if (_bad.size())
DEV_GUARDED(m_verification)
{
vector<pair<BlockInfo, bytes>> old;
DEV_GUARDED(m_verification)
swap(m_verified, old);
for (auto& b: old)
collectUnknownBad(_bad);
bool moreBad = true;
while (moreBad)
{
if (m_knownBad.count(b.first.parentHash))
{
m_knownBad.insert(b.first.hash());
m_readySet.erase(b.first.hash());
}
else
DEV_GUARDED(m_verification)
moreBad = false;
std::vector<VerifiedBlock> oldVerified;
swap(m_verified, oldVerified);
for (auto& b: oldVerified)
if (m_knownBad.count(b.verified.info.parentHash) || m_knownBad.count(b.verified.info.hash()))
{
m_knownBad.insert(b.verified.info.hash());
m_readySet.erase(b.verified.info.hash());
collectUnknownBad(b.verified.info.hash());
moreBad = true;
}
else
m_verified.push_back(std::move(b));
std::deque<UnverifiedBlock> oldUnverified;
swap(m_unverified, oldUnverified);
for (auto& b: oldUnverified)
if (m_knownBad.count(b.parentHash) || m_knownBad.count(b.hash))
{
m_knownBad.insert(b.hash);
m_readySet.erase(b.hash);
collectUnknownBad(b.hash);
moreBad = true;
}
else
m_unverified.push_back(std::move(b));
std::deque<VerifiedBlock> oldVerifying;
swap(m_verifying, oldVerifying);
for (auto& b: oldVerifying)
if (m_knownBad.count(b.verified.info.parentHash) || m_knownBad.count(b.verified.info.mixHash))
{
h256 const& h = b.blockData.size() != 0 ? b.verified.info.hash() : b.verified.info.mixHash;
m_knownBad.insert(h);
m_readySet.erase(h);
collectUnknownBad(h);
moreBad = true;
}
else
m_verifying.push_back(std::move(b));
}
}
DEV_INVARIANT_CHECK;
}
void BlockQueue::collectUnknownBad(h256 const& _bad)
{
list<h256> badQueue(1, _bad);
while (!badQueue.empty())
{
auto r = m_unknown.equal_range(badQueue.front());
badQueue.pop_front();
for (auto it = r.first; it != r.second; ++it)
{
m_unknownSize -= it->second.second.size();
m_unknownCount--;
auto newBad = it->second.first;
m_unknownSet.erase(newBad);
m_knownBad.insert(newBad);
badQueue.push_back(newBad);
}
m_unknown.erase(r.first, r.second);
}
m_knownBad += _bad;
return !m_readySet.empty();
}
bool BlockQueue::doneDrain(h256s const& _bad)
{
WriteGuard l(m_lock);
DEV_INVARIANT_CHECK;
m_drainingSet.clear();
if (_bad.size())
{
// at least one of them was bad.
m_knownBad += _bad;
for (h256 const& b : _bad)
updateBad(b);
} return !m_readySet.empty();
}
void BlockQueue::tick(BlockChain const& _bc)
@ -317,7 +380,11 @@ void BlockQueue::tick(BlockChain const& _bc)
DEV_INVARIANT_CHECK;
auto end = m_future.lower_bound(t);
for (auto i = m_future.begin(); i != end; ++i)
{
m_unknownSize -= i->second.second.size();
m_unknownCount--;
todo.push_back(move(i->second));
}
m_future.erase(m_future.begin(), end);
}
}
@ -348,12 +415,24 @@ QueueStatus BlockQueue::blockStatus(h256 const& _h) const
QueueStatus::Unknown;
}
void BlockQueue::drain(std::vector<std::pair<BlockInfo, bytes>>& o_out, unsigned _max)
bool BlockQueue::knownFull() const
{
return m_knownSize > c_maxKnownSize || m_knownCount > c_maxKnownCount;
}
bool BlockQueue::unknownFull() const
{
return m_unknownSize > c_maxUnknownSize || m_unknownCount > c_maxUnknownCount;
}
void BlockQueue::drain(VerifiedBlocks& o_out, unsigned _max)
{
WriteGuard l(m_lock);
DEV_INVARIANT_CHECK;
if (m_drainingSet.empty())
{
bool wasFull = knownFull();
DEV_GUARDED(m_verification)
{
o_out.resize(min<unsigned>(_max, m_verified.size()));
@ -364,11 +443,16 @@ void BlockQueue::drain(std::vector<std::pair<BlockInfo, bytes>>& o_out, unsigned
for (auto const& bs: o_out)
{
// TODO: @optimise use map<h256, bytes> rather than vector<bytes> & set<h256>.
auto h = bs.first.hash();
auto h = bs.verified.info.hash();
m_drainingSet.insert(h);
m_readySet.erase(h);
m_knownSize -= bs.verified.block.size();
m_knownCount--;
}
if (wasFull && !knownFull())
m_onRoomAvailable();
}
}
bool BlockQueue::invariants() const
@ -389,7 +473,11 @@ void BlockQueue::noteReady_WITH_LOCK(h256 const& _good)
for (auto it = r.first; it != r.second; ++it)
{
DEV_GUARDED(m_verification)
m_unverified.push_back(it->second);
m_unverified.push_back(UnverifiedBlock { it->second.first, it->first, it->second.second });
m_knownSize += it->second.second.size();
m_knownCount++;
m_unknownSize -= it->second.second.size();
m_unknownCount--;
auto newReady = it->second.first;
m_unknownSet.erase(newReady);
m_readySet.insert(newReady);
@ -400,6 +488,7 @@ void BlockQueue::noteReady_WITH_LOCK(h256 const& _good)
}
if (notify)
m_moreToVerify.notify_all();
DEV_INVARIANT_CHECK;
}
void BlockQueue::retryAllUnknown()
@ -409,18 +498,23 @@ void BlockQueue::retryAllUnknown()
for (auto it = m_unknown.begin(); it != m_unknown.end(); ++it)
{
DEV_GUARDED(m_verification)
m_unverified.push_back(it->second);
m_unverified.push_back(UnverifiedBlock { it->second.first, it->first, it->second.second });
auto newReady = it->second.first;
m_unknownSet.erase(newReady);
m_readySet.insert(newReady);
m_knownCount++;
m_moreToVerify.notify_one();
}
m_unknown.clear();
m_knownSize += m_unknownSize;
m_unknownSize = 0;
m_unknownCount = 0;
m_moreToVerify.notify_all();
}
std::ostream& dev::eth::operator<<(std::ostream& _out, BlockQueueStatus const& _bqs)
{
_out << "importing: " << _bqs.importing << endl;
_out << "verified: " << _bqs.verified << endl;
_out << "verifying: " << _bqs.verifying << endl;
_out << "unverified: " << _bqs.unverified << endl;

38
libethereum/BlockQueue.h

@ -31,6 +31,7 @@
#include <libdevcore/Guards.h>
#include <libethcore/Common.h>
#include <libethcore/BlockInfo.h>
#include "VerifiedBlock.h"
namespace dev
{
@ -45,6 +46,7 @@ struct BlockQueueChannel: public LogChannel { static const char* name(); static
struct BlockQueueStatus
{
size_t importing;
size_t verified;
size_t verifying;
size_t unverified;
@ -74,14 +76,14 @@ public:
~BlockQueue();
/// Import a block into the queue.
ImportResult import(bytesConstRef _tx, BlockChain const& _bc, bool _isOurs = false);
ImportResult import(bytesConstRef _block, BlockChain const& _bc, bool _isOurs = false);
/// Notes that time has moved on and some blocks that used to be "in the future" may no be valid.
void tick(BlockChain const& _bc);
/// Grabs at most @a _max of the blocks that are ready, giving them in the correct order for insertion into the chain.
/// Don't forget to call doneDrain() once you're done importing.
void drain(std::vector<std::pair<BlockInfo, bytes>>& o_out, unsigned _max);
void drain(std::vector<VerifiedBlock>& o_out, unsigned _max);
/// Must be called after a drain() call. Notes that the drained blocks have been imported into the blockchain, so we can forget about them.
/// @returns true iff there are additional blocks ready to be processed.
@ -97,25 +99,40 @@ public:
std::pair<unsigned, unsigned> items() const { ReadGuard l(m_lock); return std::make_pair(m_readySet.size(), m_unknownSet.size()); }
/// Clear everything.
void clear() { WriteGuard l(m_lock); DEV_INVARIANT_CHECK; Guard l2(m_verification); m_readySet.clear(); m_drainingSet.clear(); m_verified.clear(); m_unverified.clear(); m_unknownSet.clear(); m_unknown.clear(); m_future.clear(); }
void clear();
/// Return first block with an unknown parent.
h256 firstUnknown() const { ReadGuard l(m_lock); return m_unknownSet.size() ? *m_unknownSet.begin() : h256(); }
/// Get some infomration on the current status.
BlockQueueStatus status() const { ReadGuard l(m_lock); Guard l2(m_verification); return BlockQueueStatus{m_verified.size(), m_verifying.size(), m_unverified.size(), m_future.size(), m_unknown.size(), m_knownBad.size()}; }
BlockQueueStatus status() const { ReadGuard l(m_lock); Guard l2(m_verification); return BlockQueueStatus{m_drainingSet.size(), m_verified.size(), m_verifying.size(), m_unverified.size(), m_future.size(), m_unknown.size(), m_knownBad.size()}; }
/// Get some infomration on the given block's status regarding us.
QueueStatus blockStatus(h256 const& _h) const;
template <class T> Handler onReady(T const& _t) { return m_onReady.add(_t); }
template <class T> Handler onRoomAvailable(T const& _t) { return m_onRoomAvailable.add(_t); }
template <class T> void setOnBad(T const& _t) { m_onBad = _t; }
bool knownFull() const;
bool unknownFull() const;
private:
struct UnverifiedBlock
{
h256 hash;
h256 parentHash;
bytes block;
};
void noteReady_WITH_LOCK(h256 const& _b);
bool invariants() const override;
void verifierBody();
void collectUnknownBad(h256 const& _bad);
void updateBad(h256 const& _bad);
mutable boost::shared_mutex m_lock; ///< General lock for the sets, m_future and m_unknown.
h256Hash m_drainingSet; ///< All blocks being imported.
@ -125,15 +142,22 @@ private:
h256Hash m_knownBad; ///< Set of blocks that we know will never be valid.
std::multimap<unsigned, std::pair<h256, bytes>> m_future; ///< Set of blocks that are not yet valid. Ordered by timestamp
Signal m_onReady; ///< Called when a subsequent call to import blocks will return a non-empty container. Be nice and exit fast.
Signal m_onRoomAvailable; ///< Called when space for new blocks becomes availabe after a drain. Be nice and exit fast.
mutable Mutex m_verification; ///< Mutex that allows writing to m_verified, m_verifying and m_unverified.
std::condition_variable m_moreToVerify; ///< Signaled when m_unverified has a new entry.
std::vector<std::pair<BlockInfo, bytes>> m_verified; ///< List of blocks, in correct order, verified and ready for chain-import.
std::deque<std::pair<BlockInfo, bytes>> m_verifying; ///< List of blocks being verified; as long as the second component (bytes) is empty, it's not finished.
std::deque<std::pair<h256, bytes>> m_unverified; ///< List of blocks, in correct order, ready for verification.
std::vector<VerifiedBlock> m_verified; ///< List of blocks, in correct order, verified and ready for chain-import.
std::deque<VerifiedBlock> m_verifying; ///< List of blocks being verified; as long as the block component (bytes) is empty, it's not finished.
std::deque<UnverifiedBlock> m_unverified; ///< List of <block hash, parent hash, block data> in correct order, ready for verification.
std::vector<std::thread> m_verifiers; ///< Threads who only verify.
bool m_deleting = false; ///< Exit condition for verifiers.
std::function<void(Exception&)> m_onBad; ///< Called if we have a block that doesn't verify.
std::atomic<size_t> m_unknownSize; ///< Tracks total size in bytes of all unknown blocks
std::atomic<size_t> m_knownSize; ///< Tracks total size in bytes of all known blocks;
std::atomic<size_t> m_unknownCount; ///< Tracks total count of unknown blocks. Used to avoid additional syncing
std::atomic<size_t> m_knownCount; ///< Tracks total count of known blocks. Used to avoid additional syncing
};
std::ostream& operator<<(std::ostream& _out, BlockQueueStatus const& _s);

11
libethereum/CMakeLists.txt

@ -14,6 +14,10 @@ aux_source_directory(. SRC_LIST)
include_directories(BEFORE ..)
include_directories(${LEVELDB_INCLUDE_DIRS})
include_directories(${Boost_INCLUDE_DIRS})
if (JSONRPC)
include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS})
include_directories(${JSON_RPC_CPP_INCLUDE_DIRS})
endif()
set(EXECUTABLE ethereum)
@ -30,6 +34,13 @@ target_link_libraries(${EXECUTABLE} ethcore)
target_link_libraries(${EXECUTABLE} ${LEVELDB_LIBRARIES})
target_link_libraries(${EXECUTABLE} ${Boost_REGEX_LIBRARIES})
target_link_libraries(${EXECUTABLE} secp256k1)
if (JSONRPC)
target_link_libraries(${EXECUTABLE} ${JSON_RPC_CPP_CLIENT_LIBRARIES})
target_link_libraries(${EXECUTABLE} ${CURL_LIBRARIES})
if (DEFINED WIN32 AND NOT DEFINED CMAKE_COMPILER_IS_MINGW)
eth_copy_dlls(${EXECUTABLE} CURL_DLLS)
endif()
endif()
if (CMAKE_COMPILER_IS_MINGW)
target_link_libraries(${EXECUTABLE} ssp shlwapi)

280
libethereum/Client.cpp

@ -25,9 +25,16 @@
#include <thread>
#include <boost/filesystem.hpp>
#include <boost/math/distributions/normal.hpp>
#if ETH_JSONRPC || !ETH_TRUE
#include <jsonrpccpp/client.h>
#include <jsonrpccpp/client/connectors/httpclient.h>
#endif
#include <libdevcore/Log.h>
#include <libdevcore/StructuredLogger.h>
#include <libp2p/Host.h>
#if ETH_JSONRPC || !ETH_TRUE
#include "Sentinel.h"
#endif
#include "Defaults.h"
#include "Executive.h"
#include "EthereumHost.h"
@ -80,6 +87,114 @@ void VersionChecker::setOk()
}
}
void Client::onBadBlock(Exception& _ex) const
{
// BAD BLOCK!!!
bytes const* block = boost::get_error_info<errinfo_block>(_ex);
if (!block)
{
cwarn << "ODD: onBadBlock called but exception has no block in it.";
return;
}
badBlock(*block, _ex.what());
#if ETH_JSONRPC || !ETH_TRUE
Json::Value report;
report["client"] = "cpp";
report["version"] = Version;
report["protocolVersion"] = c_protocolVersion;
report["databaseVersion"] = c_databaseVersion;
report["errortype"] = _ex.what();
report["block"] = toHex(*block);
// add the various hints.
if (unsigned const* uncleIndex = boost::get_error_info<errinfo_uncleIndex>(_ex))
{
// uncle that failed.
report["hints"]["uncleIndex"] = *uncleIndex;
}
else if (unsigned const* txIndex = boost::get_error_info<errinfo_transactionIndex>(_ex))
{
// transaction that failed.
report["hints"]["transactionIndex"] = *txIndex;
}
else
{
// general block failure.
}
if (string const* vmtraceJson = boost::get_error_info<errinfo_vmtrace>(_ex))
Json::Reader().parse(*vmtraceJson, report["hints"]["vmtrace"]);
if (vector<bytes> const* receipts = boost::get_error_info<errinfo_receipts>(_ex))
{
report["hints"]["receipts"] = Json::arrayValue;
for (auto const& r: *receipts)
report["hints"]["receipts"].append(toHex(r));
}
if (h256Hash const* excluded = boost::get_error_info<errinfo_unclesExcluded>(_ex))
{
report["hints"]["unclesExcluded"] = Json::arrayValue;
for (auto const& r: h256Set() + *excluded)
report["hints"]["unclesExcluded"].append(Json::Value(r.hex()));
}
#define DEV_HINT_ERRINFO(X) \
if (auto const* n = boost::get_error_info<errinfo_ ## X>(_ex)) \
report["hints"][#X] = toString(*n)
#define DEV_HINT_ERRINFO_HASH(X) \
if (auto const* n = boost::get_error_info<errinfo_ ## X>(_ex)) \
report["hints"][#X] = n->hex()
DEV_HINT_ERRINFO_HASH(hash256);
DEV_HINT_ERRINFO(uncleNumber);
DEV_HINT_ERRINFO(currentNumber);
DEV_HINT_ERRINFO(now);
DEV_HINT_ERRINFO(invalidSymbol);
DEV_HINT_ERRINFO(wrongAddress);
DEV_HINT_ERRINFO(comment);
DEV_HINT_ERRINFO(min);
DEV_HINT_ERRINFO(max);
DEV_HINT_ERRINFO(name);
DEV_HINT_ERRINFO(field);
DEV_HINT_ERRINFO(data);
DEV_HINT_ERRINFO_HASH(nonce);
DEV_HINT_ERRINFO(difficulty);
DEV_HINT_ERRINFO(target);
DEV_HINT_ERRINFO_HASH(seedHash);
DEV_HINT_ERRINFO_HASH(mixHash);
if (tuple<h256, h256> const* r = boost::get_error_info<errinfo_ethashResult>(_ex))
{
report["hints"]["ethashResult"]["value"] = get<0>(*r).hex();
report["hints"]["ethashResult"]["mixHash"] = get<1>(*r).hex();
}
DEV_HINT_ERRINFO(required);
DEV_HINT_ERRINFO(got);
DEV_HINT_ERRINFO_HASH(required_LogBloom);
DEV_HINT_ERRINFO_HASH(got_LogBloom);
DEV_HINT_ERRINFO_HASH(required_h256);
DEV_HINT_ERRINFO_HASH(got_h256);
cwarn << ("Report: \n" + Json::StyledWriter().write(report));
if (!m_sentinel.empty())
{
jsonrpc::HttpClient client(m_sentinel);
Sentinel rpc(client);
try
{
rpc.eth_badBlock(report);
}
catch (...)
{
cwarn << "Error reporting to sentinel. Sure the address" << m_sentinel << "is correct?";
}
}
#endif
}
void BasicGasPricer::update(BlockChain const& _bc)
{
unsigned c = 0;
@ -174,7 +289,7 @@ Client::Client(p2p::Host* _extNet, std::string const& _dbPath, WithExisting _for
}
Client::Client(p2p::Host* _extNet, std::shared_ptr<GasPricer> _gp, std::string const& _dbPath, WithExisting _forceAction, u256 _networkId):
Worker("eth"),
Worker("eth", 0),
m_vc(_dbPath),
m_bc(_dbPath, max(m_vc.action(), _forceAction), [](unsigned d, unsigned t){ cerr << "REVISING BLOCKCHAIN: Processed " << d << " of " << t << "...\r"; }),
m_gp(_gp),
@ -185,6 +300,8 @@ Client::Client(p2p::Host* _extNet, std::shared_ptr<GasPricer> _gp, std::string c
m_lastGetWork = std::chrono::system_clock::now() - chrono::seconds(30);
m_tqReady = m_tq.onReady([=](){ this->onTransactionQueueReady(); }); // TODO: should read m_tq->onReady(thisThread, syncTransactionQueue);
m_bqReady = m_bq.onReady([=](){ this->onBlockQueueReady(); }); // TODO: should read m_bq->onReady(thisThread, syncBlockQueue);
m_bq.setOnBad([=](Exception& ex){ this->onBadBlock(ex); });
m_bc.setOnBad([=](Exception& ex){ this->onBadBlock(ex); });
m_farm.onSolutionFound([=](ProofOfWork::Solution const& s){ return this->submitWork(s); });
m_gp->update(m_bc);
@ -206,6 +323,18 @@ Client::~Client()
stopWorking();
}
static const Address c_canary("0x");
bool Client::isChainBad() const
{
return stateAt(c_canary, 0) != 0;
}
bool Client::isUpgradeNeeded() const
{
return stateAt(c_canary, 0) == 2;
}
void Client::setNetworkId(u256 _n)
{
if (auto h = m_host.lock())
@ -299,21 +428,17 @@ void Client::killChain()
void Client::clearPending()
{
h256Hash changeds;
DEV_WRITE_GUARDED(x_postMine)
{
if (!m_postMine.pending().size())
return;
// for (unsigned i = 0; i < m_postMine.pending().size(); ++i)
// appendFromNewPending(m_postMine.logBloom(i), changeds);
changeds.insert(PendingChangedFilter);
m_tq.clear();
DEV_READ_GUARDED(x_preMine)
m_postMine = m_preMine;
}
startMining();
h256Hash changeds;
noteChanged(changeds);
}
@ -336,47 +461,53 @@ static S& filtersStreamOut(S& _out, T const& _fs)
return _out;
}
void Client::appendFromNewPending(TransactionReceipt const& _receipt, h256Hash& io_changed, h256 _transactionHash)
void Client::appendFromNewPending(TransactionReceipt const& _receipt, h256Hash& io_changed, h256 _sha3)
{
Guard l(x_filtersWatches);
io_changed.insert(PendingChangedFilter);
m_specialFilters.at(PendingChangedFilter).push_back(_sha3);
for (pair<h256 const, InstalledFilter>& i: m_filters)
if (i.second.filter.envelops(RelativeBlock::Pending, m_bc.number() + 1))
{
// acceptable number.
auto m = i.second.filter.matches(_receipt);
if (m.size())
{
// acceptable number.
auto m = i.second.filter.matches(_receipt);
if (m.size())
{
// filter catches them
for (LogEntry const& l: m)
i.second.changes.push_back(LocalisedLogEntry(l, m_bc.number() + 1, _transactionHash));
io_changed.insert(i.first);
}
// filter catches them
for (LogEntry const& l: m)
i.second.changes.push_back(LocalisedLogEntry(l));
io_changed.insert(i.first);
}
}
}
void Client::appendFromNewBlock(h256 const& _block, h256Hash& io_changed)
{
// TODO: more precise check on whether the txs match.
auto d = m_bc.info(_block);
auto br = m_bc.receipts(_block);
auto receipts = m_bc.receipts(_block).receipts;
Guard l(x_filtersWatches);
io_changed.insert(ChainChangedFilter);
m_specialFilters.at(ChainChangedFilter).push_back(_block);
for (pair<h256 const, InstalledFilter>& i: m_filters)
if (i.second.filter.envelops(RelativeBlock::Latest, d.number) && i.second.filter.matches(d.logBloom))
// acceptable number & looks like block may contain a matching log entry.
for (size_t j = 0; j < br.receipts.size(); j++)
{
// acceptable number & looks like block may contain a matching log entry.
unsigned logIndex = 0;
for (size_t j = 0; j < receipts.size(); j++)
{
logIndex++;
auto tr = receipts[j];
auto m = i.second.filter.matches(tr);
if (m.size())
{
auto tr = br.receipts[j];
auto m = i.second.filter.matches(tr);
if (m.size())
{
auto transactionHash = transaction(d.hash(), j).sha3();
// filter catches them
for (LogEntry const& l: m)
i.second.changes.push_back(LocalisedLogEntry(l, (unsigned)d.number, transactionHash));
io_changed.insert(i.first);
}
auto transactionHash = transaction(d.hash(), j).sha3();
// filter catches them
for (LogEntry const& l: m)
i.second.changes.push_back(LocalisedLogEntry(l, d, transactionHash, j, logIndex));
io_changed.insert(i.first);
}
}
}
}
void Client::setForceMining(bool _enable)
@ -448,6 +579,9 @@ ProofOfWork::WorkPackage Client::getWork()
bool oldShould = shouldServeWork();
m_lastGetWork = chrono::system_clock::now();
if (!m_mineOnBadChain && isChainBad())
return ProofOfWork::WorkPackage();
// if this request has made us bother to serve work, prep it now.
if (!oldShould && shouldServeWork())
onPostStateChanged();
@ -480,13 +614,10 @@ bool Client::submitWork(ProofOfWork::Solution const& _solution)
void Client::syncBlockQueue()
{
ImportRoute ir;
cwork << "BQ ==> CHAIN ==> STATE";
{
tie(ir.first, ir.second, m_syncBlockQueue) = m_bc.sync(m_bq, m_stateDB, rand() % 90 + 10);
if (ir.first.empty())
return;
}
tie(ir.first, ir.second, m_syncBlockQueue) = m_bc.sync(m_bq, m_stateDB, rand() % 10 + 5);
if (ir.first.empty())
return;
onChainChanged(ir);
}
@ -511,7 +642,7 @@ void Client::syncTransactionQueue()
DEV_READ_GUARDED(x_postMine)
for (size_t i = 0; i < newPendingReceipts.size(); i++)
appendFromNewPending(newPendingReceipts[i], changeds, m_postMine.pending()[i].sha3());
changeds.insert(PendingChangedFilter);
// Tell farm about new transaction (i.e. restartProofOfWork mining).
onPostStateChanged();
@ -554,7 +685,6 @@ void Client::onChainChanged(ImportRoute const& _ir)
h256Hash changeds;
for (auto const& h: _ir.first)
appendFromNewBlock(h, changeds);
changeds.insert(ChainChangedFilter);
// RESTART MINING
@ -605,11 +735,22 @@ bool Client::remoteActive() const
void Client::onPostStateChanged()
{
cnote << "Post state changed";
cnote << "Post state changed.";
rejigMining();
m_remoteWorking = false;
}
if (m_bq.items().first == 0 && (isMining() || remoteActive()))
void Client::startMining()
{
m_wouldMine = true;
rejigMining();
}
void Client::rejigMining()
{
if ((wouldMine() || remoteActive()) && !m_bq.items().first && (!isChainBad() || mineOnBadChain()) /*&& (forceMining() || transactionsWaiting())*/)
{
cnote << "Restarting mining...";
cnote << "Rejigging mining...";
DEV_WRITE_GUARDED(x_working)
m_working.commitToMine(m_bc);
DEV_READ_GUARDED(x_working)
@ -618,20 +759,21 @@ void Client::onPostStateChanged()
m_postMine = m_working;
m_miningInfo = m_postMine.info();
}
m_farm.setWork(m_miningInfo);
Ethash::ensurePrecomputed(m_bc.number());
}
m_remoteWorking = false;
}
if (m_wouldMine)
{
m_farm.setWork(m_miningInfo);
if (m_turboMining)
m_farm.startGPU();
else
m_farm.startCPU();
void Client::startMining()
{
if (m_turboMining)
m_farm.startGPU();
else
m_farm.startCPU();
onPostStateChanged();
m_farm.setWork(m_miningInfo);
Ethash::ensurePrecomputed(m_bc.number());
}
}
if (!m_wouldMine)
m_farm.stop();
}
void Client::noteChanged(h256Hash const& _filters)
@ -648,15 +790,18 @@ void Client::noteChanged(h256Hash const& _filters)
cwatch << "!!!" << w.first << w.second.id.abridged();
w.second.changes += m_filters.at(w.second.id).changes;
}
else
{
cwatch << "!!!" << w.first << LogTag::Special << (w.second.id == PendingChangedFilter ? "pending" : w.second.id == ChainChangedFilter ? "chain" : "???");
w.second.changes.push_back(LocalisedLogEntry(SpecialLogEntry, 0));
}
else if (m_specialFilters.count(w.second.id))
for (h256 const& hash: m_specialFilters.at(w.second.id))
{
cwatch << "!!!" << w.first << LogTag::Special << (w.second.id == PendingChangedFilter ? "pending" : w.second.id == ChainChangedFilter ? "chain" : "???");
w.second.changes.push_back(LocalisedLogEntry(SpecialLogEntry, hash));
}
}
// clear the filters now.
for (auto& i: m_filters)
i.second.changes.clear();
for (auto& i: m_specialFilters)
i.second.clear();
}
void Client::doWork()
@ -716,7 +861,16 @@ void Client::checkWatchGarbage()
State Client::asOf(h256 const& _block) const
{
return State(m_stateDB, bc(), _block);
try
{
return State(m_stateDB, bc(), _block);
}
catch (Exception& ex)
{
ex << errinfo_block(bc().block(_block));
onBadBlock(ex);
return State();
}
}
void Client::prepareForTransaction()
@ -746,3 +900,9 @@ void Client::flushTransactions()
{
doWork();
}
HashChainStatus Client::hashChainStatus() const
{
auto h = m_host.lock();
return h ? h->status() : HashChainStatus { 0, 0, false };
}

33
libethereum/Client.h

@ -143,7 +143,7 @@ public:
ExecutionResult call(Address _dest, bytes const& _data = bytes(), u256 _gas = 125000, u256 _value = 0, u256 _gasPrice = 1 * ether, Address const& _from = Address());
/// Get the remaining gas limit in this block.
virtual u256 gasLimitRemaining() const { return m_postMine.gasLimitRemaining(); }
virtual u256 gasLimitRemaining() const override { return m_postMine.gasLimitRemaining(); }
// [PRIVATE API - only relevant for base clients, not available in general]
dev::eth::State state(unsigned _txi, h256 _block) const;
@ -156,12 +156,14 @@ public:
CanonBlockChain const& blockChain() const { return m_bc; }
/// Get some information on the block queue.
BlockQueueStatus blockQueueStatus() const { return m_bq.status(); }
/// Get some information on the block queue.
HashChainStatus hashChainStatus() const;
/// Get the block queue.
BlockQueue const& blockQueue() const { return m_bq; }
// Mining stuff:
void setAddress(Address _us) { WriteGuard l(x_preMine); m_preMine.setAddress(_us); }
virtual void setAddress(Address _us) override { WriteGuard l(x_preMine); m_preMine.setAddress(_us); }
/// Check block validity prior to mining.
bool miningParanoia() const { return m_paranoia; }
@ -176,14 +178,26 @@ public:
/// Enable/disable GPU mining.
void setTurboMining(bool _enable = true) { m_turboMining = _enable; if (isMining()) startMining(); }
/// Check to see if we'd mine on an apparently bad chain.
bool mineOnBadChain() const { return m_mineOnBadChain; }
/// Set true if you want to mine even when the canary says you're on the wrong chain.
void setMineOnBadChain(bool _v) { m_mineOnBadChain = _v; }
/// @returns true if the canary says that the chain is bad.
bool isChainBad() const;
/// @returns true if the canary says that the client should be upgraded.
bool isUpgradeNeeded() const;
/// Start mining.
/// NOT thread-safe - call it & stopMining only from a single thread
void startMining() override;
/// Stop mining.
/// NOT thread-safe
void stopMining() override { m_farm.stop(); }
void stopMining() override { m_wouldMine = false; rejigMining(); }
/// Are we mining now?
bool isMining() const override { return m_farm.isMining(); }
/// Are we mining now?
bool wouldMine() const override { return m_wouldMine; }
/// The hashrate...
uint64_t hashrate() const override;
/// Check the progress of the mining.
@ -215,6 +229,8 @@ public:
void retryUnkonwn() { m_bq.retryAllUnknown(); }
/// Get a report of activity.
ActivityReport activityReport() { ActivityReport ret; std::swap(m_report, ret); return ret; }
/// Set a JSONRPC server to which we can report bad blocks.
void setSentinel(std::string const& _server) { m_sentinel = _server; }
protected:
/// InterfaceStub methods
@ -251,6 +267,9 @@ private:
/// Called when Worker is exiting.
void doneWorking() override;
/// Called when wouldMine(), turboMining(), isChainBad(), forceMining(), pendingTransactions() have changed.
void rejigMining();
/// Magically called when the chain has changed. An import route is provided.
/// Called by either submitWork() or in our main thread through syncBlockQueue().
void onChainChanged(ImportRoute const& _ir);
@ -280,6 +299,10 @@ private:
/// @returns true only if it's worth bothering to prep the mining block.
bool shouldServeWork() const { return m_bq.items().first == 0 && (isMining() || remoteActive()); }
/// Called when we have attempted to import a bad block.
/// @warning May be called from any thread.
void onBadBlock(Exception& _ex) const;
VersionChecker m_vc; ///< Dummy object to check & update the protocol version.
CanonBlockChain m_bc; ///< Maintains block database.
BlockQueue m_bq; ///< Maintains a list of incoming blocks not yet on the blockchain (to be imported).
@ -304,8 +327,10 @@ private:
Handler m_tqReady;
Handler m_bqReady;
bool m_wouldMine = false; ///< True if we /should/ be mining.
bool m_turboMining = false; ///< Don't squander all of our time mining actually just sleeping.
bool m_forceMining = false; ///< Mine even when there are no transactions pending?
bool m_mineOnBadChain = false; ///< Mine even when the canary says it's a bad chain.
bool m_paranoia = false; ///< Should we be paranoid about our state?
mutable std::chrono::system_clock::time_point m_lastGarbageCollection;
@ -319,6 +344,8 @@ private:
Mutex x_signalled;
std::atomic<bool> m_syncTransactionQueue = {false};
std::atomic<bool> m_syncBlockQueue = {false};
std::string m_sentinel;
};
}

38
libethereum/ClientBase.cpp

@ -186,8 +186,8 @@ LocalisedLogEntries ClientBase::logs(unsigned _watchId) const
LocalisedLogEntries ClientBase::logs(LogFilter const& _f) const
{
LocalisedLogEntries ret;
unsigned begin = min<unsigned>(bc().number() + 1, (unsigned)_f.latest());
unsigned end = min(bc().number(), min(begin, (unsigned)_f.earliest()));
unsigned begin = min(bc().number() + 1, (unsigned)numberFromHash(_f.latest()));
unsigned end = min(bc().number(), min(begin, (unsigned)numberFromHash(_f.earliest())));
// Handle pending transactions differently as they're not on the block chain.
if (begin > bc().number())
@ -197,11 +197,10 @@ LocalisedLogEntries ClientBase::logs(LogFilter const& _f) const
{
// Might have a transaction that contains a matching log.
TransactionReceipt const& tr = temp.receipt(i);
auto th = temp.pending()[i].sha3();
LogEntries le = _f.matches(tr);
if (le.size())
for (unsigned j = 0; j < le.size(); ++j)
ret.insert(ret.begin(), LocalisedLogEntry(le[j], begin, th));
ret.insert(ret.begin(), LocalisedLogEntry(le[j]));
}
begin = bc().number();
}
@ -216,20 +215,22 @@ LocalisedLogEntries ClientBase::logs(LogFilter const& _f) const
{
int total = 0;
auto h = bc().numberHash(n);
auto info = bc().info(h);
auto receipts = bc().receipts(h).receipts;
unsigned logIndex = 0;
for (size_t i = 0; i < receipts.size(); i++)
{
logIndex++;
TransactionReceipt receipt = receipts[i];
if (_f.matches(receipt.bloom()))
{
auto info = bc().info(h);
auto th = transaction(info.hash(), i).sha3();
LogEntries le = _f.matches(receipt);
if (le.size())
{
total += le.size();
for (unsigned j = 0; j < le.size(); ++j)
ret.insert(ret.begin(), LocalisedLogEntry(le[j], n, th));
ret.insert(ret.begin(), LocalisedLogEntry(le[j], info, th, i, logIndex));
}
}
@ -328,6 +329,8 @@ LocalisedLogEntries ClientBase::checkWatch(unsigned _watchId)
BlockInfo ClientBase::blockInfo(h256 _hash) const
{
if (_hash == PendingBlockHash)
return preMine().info();
return BlockInfo(bc().block(_hash));
}
@ -415,17 +418,16 @@ h256s ClientBase::pendingHashes() const
return h256s() + postMine().pendingHashes();
}
StateDiff ClientBase::diff(unsigned _txi, h256 _block) const
{
State st = asOf(_block);
return st.fromPending(_txi).diff(st.fromPending(_txi + 1));
return st.fromPending(_txi).diff(st.fromPending(_txi + 1), true);
}
StateDiff ClientBase::diff(unsigned _txi, BlockNumber _block) const
{
State st = asOf(_block);
return st.fromPending(_txi).diff(st.fromPending(_txi + 1));
return st.fromPending(_txi).diff(st.fromPending(_txi + 1), true);
}
Addresses ClientBase::addresses(BlockNumber _block) const
@ -457,6 +459,24 @@ h256 ClientBase::hashFromNumber(BlockNumber _number) const
BlockNumber ClientBase::numberFromHash(h256 _blockHash) const
{
if (_blockHash == PendingBlockHash)
return bc().number() + 1;
else if (_blockHash == LatestBlockHash)
return bc().number();
else if (_blockHash == EarliestBlockHash)
return 0;
return bc().number(_blockHash);
}
int ClientBase::compareBlockHashes(h256 _h1, h256 _h2) const
{
BlockNumber n1 = numberFromHash(_h1);
BlockNumber n2 = numberFromHash(_h2);
if (n1 > n2) {
return 1;
} else if (n1 == n2) {
return 0;
}
return -1;
}

13
libethereum/ClientBase.h

@ -44,7 +44,7 @@ static const h256 PendingChangedFilter = u256(0);
static const h256 ChainChangedFilter = u256(1);
static const LogEntry SpecialLogEntry = LogEntry(Address(), h256s(), bytes());
static const LocalisedLogEntry InitialChange(SpecialLogEntry, 0);
static const LocalisedLogEntry InitialChange(SpecialLogEntry);
struct ClientWatch
{
@ -118,6 +118,7 @@ public:
virtual h256 hashFromNumber(BlockNumber _number) const override;
virtual BlockNumber numberFromHash(h256 _blockHash) const override;
virtual int compareBlockHashes(h256 _h1, h256 _h2) const override;
virtual BlockInfo blockInfo(h256 _hash) const override;
virtual BlockDetails blockDetails(h256 _hash) const override;
virtual Transaction transaction(h256 _transactionHash) const override;
@ -133,8 +134,8 @@ public:
virtual Transactions pending() const override;
virtual h256s pendingHashes() const override;
ImportResult injectTransaction(bytes const& _rlp) override { prepareForTransaction(); return m_tq.import(_rlp); }
ImportResult injectBlock(bytes const& _block);
virtual ImportResult injectTransaction(bytes const& _rlp) override { prepareForTransaction(); return m_tq.import(_rlp); }
virtual ImportResult injectBlock(bytes const& _block) override;
using Interface::diff;
virtual StateDiff diff(unsigned _txi, h256 _block) const override;
@ -144,9 +145,6 @@ public:
virtual Addresses addresses(BlockNumber _block) const override;
virtual u256 gasLimitRemaining() const override;
/// Set the coinbase address
virtual void setAddress(Address _us) = 0;
/// Get the coinbase address
virtual Address address() const override;
@ -155,6 +153,7 @@ public:
virtual void startMining() override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("ClientBase::startMining")); }
virtual void stopMining() override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("ClientBase::stopMining")); }
virtual bool isMining() const override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("ClientBase::isMining")); }
virtual bool wouldMine() const override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("ClientBase::wouldMine")); }
virtual uint64_t hashrate() const override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("ClientBase::hashrate")); }
virtual MiningProgress miningProgress() const override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("ClientBase::miningProgress")); }
virtual ProofOfWork::WorkPackage getWork() override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("ClientBase::getWork")); }
@ -178,6 +177,8 @@ protected:
// filters
mutable Mutex x_filtersWatches; ///< Our lock.
std::unordered_map<h256, InstalledFilter> m_filters; ///< The dictionary of filters that are active.
std::unordered_map<h256, h256s> m_specialFilters = std::unordered_map<h256, std::vector<h256>>{{PendingChangedFilter, {}}, {ChainChangedFilter, {}}};
///< The dictionary of special filters and their additional data
std::map<unsigned, ClientWatch> m_watches; ///< Each and every watch - these reference a filter.
};

11
libethereum/CommonNet.h

@ -38,9 +38,9 @@ namespace eth
#if ETH_DEBUG
static const unsigned c_maxHashes = 2048; ///< Maximum number of hashes BlockHashes will ever send.
static const unsigned c_maxHashesAsk = 256; ///< Maximum number of hashes GetBlockHashes will ever ask for.
static const unsigned c_maxHashesAsk = 2048; ///< Maximum number of hashes GetBlockHashes will ever ask for.
static const unsigned c_maxBlocks = 128; ///< Maximum number of blocks Blocks will ever send.
static const unsigned c_maxBlocksAsk = 8; ///< Maximum number of blocks we ask to receive in Blocks (when using GetChain).
static const unsigned c_maxBlocksAsk = 128; ///< Maximum number of blocks we ask to receive in Blocks (when using GetChain).
static const unsigned c_maxPayload = 262144; ///< Maximum size of packet for us to send.
#else
static const unsigned c_maxHashes = 2048; ///< Maximum number of hashes BlockHashes will ever send.
@ -84,5 +84,12 @@ enum class Syncing
Done
};
struct HashChainStatus
{
unsigned total;
unsigned received;
bool estimated;
};
}
}

7
libethereum/DownloadMan.cpp

@ -80,7 +80,6 @@ HashDownloadSub::HashDownloadSub(HashDownloadMan& _man): m_man(&_man)
{
WriteGuard l(m_man->x_subs);
m_asked = RangeMask<unsigned>(m_man->m_chainStart, m_man->m_chainStart + m_man->m_chainCount);
m_attempted = RangeMask<unsigned>(m_man->m_chainStart, m_man->m_chainStart + m_man->m_chainCount);
m_man->m_subs.insert(this);
}
@ -98,7 +97,6 @@ void HashDownloadSub::resetFetch()
Guard l(m_fetch);
m_remaining = 0;
m_asked = RangeMask<unsigned>(m_man->m_chainStart, m_man->m_chainStart + m_man->m_chainCount);
m_attempted = RangeMask<unsigned>(m_man->m_chainStart, m_man->m_chainStart + m_man->m_chainCount);
}
unsigned HashDownloadSub::nextFetch(unsigned _n)
@ -110,10 +108,9 @@ unsigned HashDownloadSub::nextFetch(unsigned _n)
if (!m_man || m_man->chainEmpty())
return 0;
m_asked = (~(m_man->taken() + m_attempted)).lowest(_n);
m_asked = (~(m_man->taken())).lowest(_n);
if (m_asked.empty())
m_asked = (~(m_man->taken(true) + m_attempted)).lowest(_n);
m_attempted += m_asked;
m_asked = (~(m_man->taken(true))).lowest(_n);
return *m_asked.begin();
}

8
libethereum/DownloadMan.h

@ -187,7 +187,6 @@ public:
bool askedContains(unsigned _i) const { Guard l(m_fetch); return m_asked.contains(_i); }
RangeMask<unsigned> const& asked() const { return m_asked; }
RangeMask<unsigned> const& attemped() const { return m_attempted; }
private:
void resetFetch(); // Called by DownloadMan when we need to reset the download.
@ -196,7 +195,6 @@ private:
mutable Mutex m_fetch;
unsigned m_remaining;
RangeMask<unsigned> m_asked;
RangeMask<unsigned> m_attempted;
};
class HashDownloadMan
@ -255,6 +253,11 @@ public:
return m_got.full();
}
unsigned gotCount() const
{
return m_got.size();
}
size_t chainSize() const { ReadGuard l(m_lock); return m_chainCount; }
size_t chainEmpty() const { ReadGuard l(m_lock); return m_chainCount == 0; }
void foreachSub(std::function<void(HashDownloadSub const&)> const& _f) const { ReadGuard l(x_subs); for(auto i: m_subs) _f(*i); }
@ -274,3 +277,4 @@ private:
}
}

214
libethereum/EthereumHost.cpp

@ -39,6 +39,7 @@ using namespace dev::eth;
using namespace p2p;
unsigned const EthereumHost::c_oldProtocolVersion = 60; //TODO: remove this once v61+ is common
unsigned const c_chainReorgSize = 30000;
EthereumHost::EthereumHost(BlockChain const& _ch, TransactionQueue& _tq, BlockQueue& _bq, u256 _networkId):
HostCapability<EthereumPeer>(),
@ -50,6 +51,7 @@ EthereumHost::EthereumHost(BlockChain const& _ch, TransactionQueue& _tq, BlockQu
{
m_latestBlockSent = _ch.currentHash();
m_hashMan.reset(m_chain.number() + 1);
m_bqRoomAvailable = m_bq.onRoomAvailable([this](){ m_continueSync = true; });
}
EthereumHost::~EthereumHost()
@ -91,7 +93,7 @@ void EthereumHost::doWork()
bool netChange = ensureInitialised();
auto h = m_chain.currentHash();
// If we've finished our initial sync (including getting all the blocks into the chain so as to reduce invalid transactions), start trading transactions & blocks
if (isSyncing() && m_chain.isKnown(m_latestBlockSent))
if (!isSyncing() && m_chain.isKnown(m_latestBlockSent))
{
if (m_newTransactions)
{
@ -105,6 +107,12 @@ void EthereumHost::doWork()
}
}
if (m_continueSync)
{
m_continueSync = false;
continueSync();
}
foreachPeer([](EthereumPeer* _p) { _p->tick(); });
// return netChange;
@ -144,6 +152,7 @@ void EthereumHost::maintainTransactions()
RLPStream ts;
_p->prep(ts, TransactionsPacket, n).appendRaw(b, n);
_p->sealAndSend(ts);
cnote << "Sent" << n << "transactions to " << _p->session()->info().clientVersion;
}
_p->m_requireTransactions = false;
});
@ -237,7 +246,7 @@ void EthereumHost::maintainBlocks(h256 const& _currentHash)
void EthereumHost::onPeerStatus(EthereumPeer* _peer)
{
Guard l(x_sync);
RecursiveGuard l(x_sync);
if (_peer->m_genesisHash != m_chain.genesisHash())
_peer->disable("Invalid genesis hash");
else if (_peer->m_protocolVersion != protocolVersion() && _peer->m_protocolVersion != c_oldProtocolVersion)
@ -250,36 +259,43 @@ void EthereumHost::onPeerStatus(EthereumPeer* _peer)
_peer->disable("Peer banned for previous bad behaviour.");
else
{
if (_peer->m_protocolVersion != protocolVersion())
estimatePeerHashes(_peer);
else if (_peer->m_latestBlockNumber > m_chain.number())
_peer->m_expectedHashes = (unsigned)_peer->m_latestBlockNumber - m_chain.number() + 1000;
unsigned estimatedHashes = estimateHashes();
if (_peer->m_protocolVersion == protocolVersion())
{
if (_peer->m_latestBlockNumber > m_chain.number())
_peer->m_expectedHashes = (unsigned)_peer->m_latestBlockNumber - m_chain.number();
if (_peer->m_expectedHashes > estimatedHashes)
_peer->disable("Too many hashes");
else if (m_needSyncHashes && m_hashMan.chainSize() < _peer->m_expectedHashes)
m_hashMan.resetToRange(m_chain.number() + 1, _peer->m_expectedHashes);
}
else
_peer->m_expectedHashes = 1000;
if (m_hashMan.chainSize() < _peer->m_expectedHashes)
m_hashMan.resetToRange(m_chain.number() + 1, _peer->m_expectedHashes);
_peer->m_expectedHashes = estimatedHashes;
continueSync(_peer);
}
}
void EthereumHost::estimatePeerHashes(EthereumPeer* _peer)
unsigned EthereumHost::estimateHashes()
{
BlockInfo block = m_chain.info();
time_t lastBlockTime = (block.hash() == m_chain.genesisHash()) ? 1428192000 : (time_t)block.timestamp;
time_t now = time(0);
unsigned blockCount = 1000;
unsigned blockCount = c_chainReorgSize;
if (lastBlockTime > now)
clog(NetWarn) << "Clock skew? Latest block is in the future";
else
blockCount += (now - lastBlockTime) / (unsigned)c_durationLimit;
clog(NetAllDetail) << "Estimated hashes: " << blockCount;
_peer->m_expectedHashes = blockCount;
return blockCount;
}
void EthereumHost::onPeerHashes(EthereumPeer* _peer, h256s const& _hashes)
{
Guard l(x_sync);
assert(_peer->m_asking == Asking::Nothing);
RecursiveGuard l(x_sync);
if (_peer->m_syncHashNumber > 0)
_peer->m_syncHashNumber += _hashes.size();
_peer->setAsking(Asking::Nothing);
onPeerHashes(_peer, _hashes, false);
}
@ -287,13 +303,23 @@ void EthereumHost::onPeerHashes(EthereumPeer* _peer, h256s const& _hashes, bool
{
if (_hashes.empty())
{
onPeerDoneHashes(_peer, true);
_peer->m_hashSub.doneFetch();
continueSync();
return;
}
bool syncByNumber = _peer->m_syncHashNumber;
if (!syncByNumber && !_complete && _peer->m_syncHash != m_syncingLatestHash)
{
// Obsolete hashes, discard
continueSync(_peer);
return;
}
unsigned knowns = 0;
unsigned unknowns = 0;
h256s neededBlocks;
bool syncByNumber = !m_syncingLatestHash;
unsigned firstNumber = _peer->m_syncHashNumber - _hashes.size();
for (unsigned i = 0; i < _hashes.size(); ++i)
{
_peer->addRating(1);
@ -323,8 +349,11 @@ void EthereumHost::onPeerHashes(EthereumPeer* _peer, h256s const& _hashes, bool
}
else
knowns++;
if (!syncByNumber)
m_syncingLatestHash = h;
else
_peer->m_hashSub.noteHash(firstNumber + i, 1);
}
if (syncByNumber)
{
@ -339,7 +368,7 @@ void EthereumHost::onPeerHashes(EthereumPeer* _peer, h256s const& _hashes, bool
if (_complete)
{
m_needSyncBlocks = true;
continueSync(_peer);
continueSync();
}
else if (syncByNumber && m_hashMan.isComplete())
{
@ -370,14 +399,15 @@ void EthereumHost::onPeerDoneHashes(EthereumPeer* _peer, bool _localChain)
{
m_man.resetToChain(m_hashes);
m_hashes.clear();
m_hashMan.reset(m_chain.number() + 1);
}
continueSync();
}
void EthereumHost::onPeerBlocks(EthereumPeer* _peer, RLP const& _r)
{
Guard l(x_sync);
assert(_peer->m_asking == Asking::Nothing);
RecursiveGuard l(x_sync);
_peer->setAsking(Asking::Nothing);
unsigned itemCount = _r.itemCount();
clog(NetMessageSummary) << "Blocks (" << dec << itemCount << "entries)" << (itemCount ? "" : ": NoMoreBlocks");
@ -386,6 +416,7 @@ void EthereumHost::onPeerBlocks(EthereumPeer* _peer, RLP const& _r)
// Got to this peer's latest block - just give up.
clog(NetNote) << "Finishing blocks fetch...";
// NOTE: need to notify of giving up on chain-hashes, too, altering state as necessary.
_peer->m_sub.doneFetch();
_peer->setIdle();
return;
}
@ -437,35 +468,26 @@ void EthereumHost::onPeerBlocks(EthereumPeer* _peer, RLP const& _r)
}
clog(NetMessageSummary) << dec << success << "imported OK," << unknown << "with unknown parents," << future << "with future timestamps," << got << " already known," << repeated << " repeats received.";
if (m_man.isComplete() && !m_needSyncHashes)
{
// Done our chain-get.
m_needSyncBlocks = false;
clog(NetNote) << "Chain download complete.";
// 1/100th for each useful block hash.
_peer->addRating(m_man.chainSize() / 100); //TODO: what about other peers?
m_man.reset();
}
continueSync(_peer);
}
void EthereumHost::onPeerNewHashes(EthereumPeer* _peer, h256s const& _hashes)
{
Guard l(x_sync);
if (isSyncing_UNSAFE())
RecursiveGuard l(x_sync);
if (isSyncing_UNSAFE() || _peer->isConversing())
{
clog(NetMessageSummary) << "Ignoring new hashes since we're already downloading.";
return;
}
clog(NetNote) << "New block hash discovered: syncing without help.";
_peer->m_syncHashNumber = 0;
onPeerHashes(_peer, _hashes, true);
}
void EthereumHost::onPeerNewBlock(EthereumPeer* _peer, RLP const& _r)
{
Guard l(x_sync);
if (isSyncing_UNSAFE())
RecursiveGuard l(x_sync);
if (isSyncing_UNSAFE() || _peer->isConversing())
{
clog(NetMessageSummary) << "Ignoring new blocks since we're already downloading.";
return;
@ -519,7 +541,7 @@ void EthereumHost::onPeerNewBlock(EthereumPeer* _peer, RLP const& _r)
_peer->m_knownBlocks.insert(h);
if (sync)
continueSync(_peer);
continueSync();
}
}
@ -551,9 +573,21 @@ void EthereumHost::onPeerTransactions(EthereumPeer* _peer, RLP const& _r)
}
}
void EthereumHost::onPeerAborting(EthereumPeer* _peer)
{
RecursiveGuard l(x_sync);
if (_peer->isConversing())
{
_peer->setIdle();
if (_peer->isCriticalSyncing())
_peer->setRude();
continueSync();
}
}
void EthereumHost::continueSync()
{
clog(NetAllDetail) << "Getting help with downloading hashes and blocks";
clog(NetAllDetail) << "Continuing sync for all peers";
foreachPeer([&](EthereumPeer* _p)
{
if (_p->m_asking == Asking::Nothing)
@ -564,22 +598,43 @@ void EthereumHost::continueSync()
void EthereumHost::continueSync(EthereumPeer* _peer)
{
assert(_peer->m_asking == Asking::Nothing);
bool otherPeerSync = false;
if (m_needSyncHashes && peerShouldGrabChain(_peer))
bool otherPeerV60Sync = false;
bool otherPeerV61Sync = false;
if (m_needSyncHashes)
{
if (!peerShouldGrabChain(_peer))
{
_peer->setIdle();
return;
}
foreachPeer([&](EthereumPeer* _p)
{
if (_p != _peer && _p->m_asking == Asking::Hashes && _p->m_protocolVersion != protocolVersion())
otherPeerSync = true; // Already have a peer downloading hash chain with old protocol, do nothing
if (_p != _peer && _p->m_asking == Asking::Hashes)
{
if (_p->m_protocolVersion != protocolVersion())
otherPeerV60Sync = true; // Already have a peer downloading hash chain with old protocol, do nothing
else
otherPeerV61Sync = true; // Already have a peer downloading hash chain with V61+ protocol, join if supported
}
});
if (otherPeerSync)
if (otherPeerV60Sync && !m_hashes.empty())
{
/// Downloading from other peer with v60 protocol, nothing else we can do
_peer->setIdle();
return;
}
if (otherPeerV61Sync && _peer->m_protocolVersion != protocolVersion())
{
/// Downloading from other peer with v60 protocol, nothing ese we can do
/// Downloading from other peer with v61+ protocol which this peer does not support,
_peer->setIdle();
return;
}
if (_peer->m_protocolVersion == protocolVersion() && !m_syncingLatestHash)
if (_peer->m_protocolVersion == protocolVersion() && !m_hashMan.isComplete())
{
m_syncingV61 = true;
_peer->requestHashes(); /// v61+ and not catching up to a particular hash
}
else
{
// Restart/continue sync in single peer mode
@ -588,17 +643,63 @@ void EthereumHost::continueSync(EthereumPeer* _peer)
m_syncingLatestHash =_peer->m_latestHash;
m_syncingTotalDifficulty = _peer->m_totalDifficulty;
}
_peer->requestHashes(m_syncingLatestHash);
if (_peer->m_totalDifficulty >= m_syncingTotalDifficulty)
{
_peer->requestHashes(m_syncingLatestHash);
m_syncingV61 = false;
m_estimatedHashes = _peer->m_expectedHashes;
}
else
_peer->setIdle();
}
}
else if (m_needSyncBlocks)
{
if (m_man.isComplete())
{
// Done our chain-get.
m_needSyncBlocks = false;
clog(NetNote) << "Chain download complete.";
// 1/100th for each useful block hash.
_peer->addRating(m_man.chainSize() / 100); //TODO: what about other peers?
m_man.reset();
_peer->setIdle();
return;
}
else if (peerCanHelp(_peer))
{
// Check block queue status
if (m_bq.unknownFull())
{
clog(NetWarn) << "Too many unknown blocks, restarting sync";
m_bq.clear();
reset();
continueSync();
}
else if (m_bq.knownFull())
{
clog(NetAllDetail) << "Waiting for block queue before downloading blocks";
_peer->setIdle();
}
else
_peer->requestBlocks();
}
}
else if (m_needSyncBlocks && peerShouldGrabBlocks(_peer)) // Check if this peer can help with downloading blocks
_peer->requestBlocks();
else
_peer->setIdle();
}
bool EthereumHost::peerCanHelp(EthereumPeer* _peer) const
{
(void)_peer;
return true;
}
bool EthereumHost::peerShouldGrabBlocks(EthereumPeer* _peer) const
{
// this is only good for deciding whether to go ahead and grab a particular peer's hash chain,
// yet it's being used in determining whether to allow a peer help with downloading an existing
// chain of blocks.
auto td = _peer->m_totalDifficulty;
auto lh = m_syncingLatestHash;
auto ctd = m_chain.details().totalDifficulty;
@ -611,6 +712,10 @@ bool EthereumHost::peerShouldGrabBlocks(EthereumPeer* _peer) const
bool EthereumHost::peerShouldGrabChain(EthereumPeer* _peer) const
{
// Early exit if this peer has proved unreliable.
if (_peer->isRude())
return false;
h256 c = m_chain.currentHash();
unsigned n = m_chain.number();
u256 td = m_chain.details().totalDifficulty;
@ -630,13 +735,14 @@ bool EthereumHost::peerShouldGrabChain(EthereumPeer* _peer) const
bool EthereumHost::isSyncing_UNSAFE() const
{
/// We need actual peer information here to handle the case when we are the first ever peer on the network to mine.
/// I.e. on a new private network the first node mining has noone to sync with and should start block propogation immediately.
bool syncing = false;
foreachPeer([&](EthereumPeer* _p)
{
if (_p->m_asking != Asking::Nothing)
syncing = true;
});
return syncing;
return m_needSyncBlocks || m_needSyncHashes;
}
HashChainStatus EthereumHost::status()
{
RecursiveGuard l(x_sync);
if (m_syncingV61)
return HashChainStatus { static_cast<unsigned>(m_hashMan.chainSize()), static_cast<unsigned>(m_hashMan.gotCount()), false };
return HashChainStatus { m_estimatedHashes > 0 ? m_estimatedHashes - c_chainReorgSize : 0, static_cast<unsigned>(m_hashes.size()), m_estimatedHashes > 0 };
}

16
libethereum/EthereumHost.h

@ -70,8 +70,8 @@ public:
void reset();
DownloadMan const& downloadMan() const { return m_man; }
bool isSyncing() const { Guard l(x_sync); return isSyncing_UNSAFE(); }
bool isBanned(p2p::NodeId _id) const { return !!m_banned.count(_id); }
bool isSyncing() const { RecursiveGuard l(x_sync); return isSyncing_UNSAFE(); }
bool isBanned(p2p::NodeId const& _id) const { return !!m_banned.count(_id); }
void noteNewTransactions() { m_newTransactions = true; }
void noteNewBlocks() { m_newBlocks = true; }
@ -82,10 +82,12 @@ public:
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 onPeerTransactions(EthereumPeer* _peer, RLP const& _r); ///< Called by peer when it has new transactions
void onPeerAborting(EthereumPeer* _peer); ///< Called by peer when it is disconnecting
DownloadMan& downloadMan() { return m_man; }
HashDownloadMan& hashDownloadMan() { return m_hashMan; }
BlockChain const& chain() { return m_chain; }
HashChainStatus status();
static unsigned const c_oldProtocolVersion;
@ -122,11 +124,14 @@ private:
void onPeerHashes(EthereumPeer* _peer, h256s const& _hashes, bool _complete);
bool peerShouldGrabBlocks(EthereumPeer* _peer) const;
bool peerShouldGrabChain(EthereumPeer* _peer) const;
bool peerCanHelp(EthereumPeer* _peer) const;
unsigned estimateHashes();
void estimatePeerHashes(EthereumPeer* _peer);
BlockChain const& m_chain;
TransactionQueue& m_tq; ///< Maintains a list of incoming transactions not yet in a block on the blockchain.
BlockQueue& m_bq; ///< Maintains a list of incoming blocks not yet on the blockchain (to be imported).
Handler m_bqRoomAvailable;
u256 m_networkId;
@ -141,12 +146,15 @@ private:
bool m_newTransactions = false;
bool m_newBlocks = false;
mutable Mutex x_sync;
mutable RecursiveMutex x_sync;
bool m_needSyncHashes = true; ///< Indicates if need to downlad hashes
bool m_needSyncBlocks = true; ///< Indicates if we still need to download some blocks
h256 m_syncingLatestHash; ///< Latest block's hash, as of the current sync.
u256 m_syncingTotalDifficulty; ///< Latest block's total difficulty, as of the current sync.
h256s m_hashes; ///< List of hashes with unknown block numbers. Used for v60 chain downloading and catching up to a particular unknown
h256s m_hashes; ///< List of hashes with unknown block numbers. Used for PV60 chain downloading and catching up to a particular unknown
unsigned m_estimatedHashes = 0; ///< Number of estimated hashes for the last peer over PV60. Used for status reporting only.
bool m_syncingV61 = false; ///< True if recent activity was over pv61+. Used for status reporting only.
bool m_continueSync = false; ///< True when the block queue has processed a block; we should restart grabbing blocks.
};
}

50
libethereum/EthereumPeer.cpp

@ -25,6 +25,7 @@
#include <libdevcore/Common.h>
#include <libethcore/Exceptions.h>
#include <libp2p/Session.h>
#include <libp2p/Host.h>
#include "BlockChain.h"
#include "EthereumHost.h"
#include "TransactionQueue.h"
@ -40,6 +41,7 @@ EthereumPeer::EthereumPeer(Session* _s, HostCapabilityFace* _h, unsigned _i, Cap
m_hashSub(host()->hashDownloadMan()),
m_peerCapabilityVersion(_cap.second)
{
session()->addNote("manners", isRude() ? "RUDE" : "nice");
m_syncHashNumber = host()->chain().number() + 1;
requestStatus();
}
@ -50,10 +52,20 @@ EthereumPeer::~EthereumPeer()
abortSync();
}
bool EthereumPeer::isRude() const
{
return repMan().isRude(*session(), name());
}
void EthereumPeer::setRude()
{
repMan().noteRude(*session(), name());
session()->addNote("manners", "RUDE");
}
void EthereumPeer::abortSync()
{
if (isSyncing())
setIdle();
host()->onPeerAborting(this);
}
EthereumHost* EthereumPeer::host() const
@ -105,6 +117,7 @@ void EthereumPeer::requestHashes()
{
assert(m_asking == Asking::Nothing);
m_syncHashNumber = m_hashSub.nextFetch(c_maxHashesAsk);
m_syncHash = h256();
setAsking(Asking::Hashes);
RLPStream s;
prep(s, GetBlockHashesByNumberPacket, 2) << m_syncHashNumber << c_maxHashesAsk;
@ -119,13 +132,15 @@ void EthereumPeer::requestHashes(h256 const& _lastHash)
RLPStream s;
prep(s, GetBlockHashesPacket, 2) << _lastHash << c_maxHashesAsk;
clog(NetMessageDetail) << "Requesting block hashes staring from " << _lastHash;
m_syncHash = _lastHash;
m_syncHashNumber = 0;
sealAndSend(s);
}
void EthereumPeer::requestBlocks()
{
setAsking(Asking::Blocks);
auto blocks = m_sub.nextFetch(c_maxBlocksAsk);
auto blocks = m_sub.nextFetch(isRude() ? 1 : c_maxBlocksAsk);
if (blocks.size())
{
RLPStream s;
@ -145,7 +160,7 @@ void EthereumPeer::setAsking(Asking _a)
m_lastAsk = chrono::system_clock::now();
session()->addNote("ask", _a == Asking::Nothing ? "nothing" : _a == Asking::State ? "state" : _a == Asking::Hashes ? "hashes" : _a == Asking::Blocks ? "blocks" : "?");
session()->addNote("sync", string(isSyncing() ? "ongoing" : "holding") + (needsSyncing() ? " & needed" : ""));
session()->addNote("sync", string(isCriticalSyncing() ? "ONGOING" : "holding") + (needsSyncing() ? " & needed" : ""));
}
void EthereumPeer::tick()
@ -155,11 +170,16 @@ void EthereumPeer::tick()
session()->disconnect(PingTimeout);
}
bool EthereumPeer::isSyncing() const
bool EthereumPeer::isConversing() const
{
return m_asking != Asking::Nothing;
}
bool EthereumPeer::isCriticalSyncing() const
{
return m_asking == Asking::Hashes || m_asking == Asking::State || (m_asking == Asking::Blocks && m_protocolVersion == 60);
}
bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
{
try
@ -175,8 +195,16 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
m_genesisHash = _r[4].toHash<h256>();
if (m_peerCapabilityVersion == host()->protocolVersion())
{
m_protocolVersion = host()->protocolVersion();
m_latestBlockNumber = _r[5].toInt<u256>();
if (_r.itemCount() != 6)
{
clog(NetImpolite) << "Peer does not support PV61+ status extension.";
m_protocolVersion = EthereumHost::c_oldProtocolVersion;
}
else
{
m_protocolVersion = host()->protocolVersion();
m_latestBlockNumber = _r[5].toInt<u256>();
}
}
clog(NetMessageSummary) << "Status:" << m_protocolVersion << "/" << m_networkId << "/" << m_genesisHash << "/" << m_latestBlockNumber << ", TD:" << m_totalDifficulty << "=" << m_latestHash;
@ -237,15 +265,10 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
clog(NetWarn) << "Peer giving us hashes when we didn't ask for them.";
break;
}
setAsking(Asking::Nothing);
h256s hashes(itemCount);
for (unsigned i = 0; i < itemCount; ++i)
{
hashes[i] = _r[i].toHash<h256>();
m_hashSub.noteHash(m_syncHashNumber + i, 1);
}
m_syncHashNumber += itemCount;
host()->onPeerHashes(this, hashes);
break;
}
@ -288,10 +311,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
if (m_asking != Asking::Blocks)
clog(NetImpolite) << "Peer giving us blocks when we didn't ask for them.";
else
{
setAsking(Asking::Nothing);
host()->onPeerBlocks(this, _r);
}
break;
}
case NewBlockPacket:

20
libethereum/EthereumPeer.h

@ -82,6 +82,12 @@ public:
/// Request blocks. Uses block download manager.
void requestBlocks();
/// Check if this node is rude.
bool isRude() const;
/// Set that it's a rude node.
void setRude();
private:
using p2p::Capability::sealAndSend;
@ -101,10 +107,13 @@ private:
void setAsking(Asking _g);
/// Do we presently need syncing with this peer?
bool needsSyncing() const { return !!m_latestHash; }
bool needsSyncing() const { return !isRude() && !!m_latestHash; }
/// Are we presently in the process of communicating with this peer?
bool isConversing() const;
/// Are we presently syncing with this peer?
bool isSyncing() const;
/// Are we presently in a critical part of the syncing process with this peer?
bool isCriticalSyncing() const;
/// Runs period checks to check up on the peer.
void tick();
@ -128,8 +137,9 @@ private:
/// This is built as we ask for hashes. Once no more hashes are given, we present this to the
/// host who initialises the DownloadMan and m_sub becomes active for us to begin asking for blocks.
unsigned m_expectedHashes = 0; ///< Estimated upper bound of hashes to expect from this peer.
unsigned m_syncHashNumber = 0; ///< Number of latest hash we sync to
unsigned m_expectedHashes = 0; ///< Estimated upper bound of hashes to expect from this peer.
unsigned m_syncHashNumber = 0; ///< Number of latest hash we sync to (PV61+)
h256 m_syncHash; ///< Latest hash we sync to (PV60)
/// Once we're asking for blocks, this becomes in use.
DownloadSub m_sub;

141
libethereum/Executive.cpp

@ -19,6 +19,9 @@
#include "Executive.h"
#include <boost/timer.hpp>
#if ETH_JSONRPC || !ETH_TRUE
#include <json/json.h>
#endif
#include <libdevcore/CommonIO.h>
#include <libevm/VMFactory.h>
#include <libevm/VM.h>
@ -34,6 +37,111 @@ using namespace dev::eth;
const char* VMTraceChannel::name() { return "EVM"; }
const char* ExecutiveWarnChannel::name() { return WarnChannel::name(); }
StandardTrace::StandardTrace():
m_trace(new Json::Value(Json::arrayValue))
{}
bool changesMemory(Instruction _inst)
{
return
_inst == Instruction::MSTORE ||
_inst == Instruction::MSTORE8 ||
_inst == Instruction::MLOAD ||
_inst == Instruction::CREATE ||
_inst == Instruction::CALL ||
_inst == Instruction::CALLCODE ||
_inst == Instruction::SHA3 ||
_inst == Instruction::CALLDATACOPY ||
_inst == Instruction::CODECOPY ||
_inst == Instruction::EXTCODECOPY;
}
bool changesStorage(Instruction _inst)
{
return _inst == Instruction::SSTORE;
}
void StandardTrace::operator()(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;
Json::Value r(Json::objectValue);
Json::Value stack(Json::arrayValue);
for (auto const& i: vm.stack())
stack.append(toHex(toCompactBigEndian(i), 1));
r["stack"] = stack;
bool returned = false;
bool newContext = false;
Instruction lastInst = Instruction::STOP;
if (m_lastInst.size() == ext.depth)
{
// starting a new context
assert(m_lastInst.size() == ext.depth);
m_lastInst.push_back(inst);
newContext = true;
}
else if (m_lastInst.size() == ext.depth + 2)
{
// returned from old context
returned = true;
m_lastInst.pop_back();
lastInst = m_lastInst.back();
}
else if (m_lastInst.size() == ext.depth + 1)
{
// continuing in previous context
lastInst = m_lastInst.back();
m_lastInst.back() = inst;
}
else
{
cwarn << "GAA!!! Tracing VM and more than one new/deleted stack frame between steps!";
cwarn << "Attmepting naive recovery...";
m_lastInst.resize(ext.depth + 1);
}
if (changesMemory(lastInst) || newContext)
{
if (vm.memory().size() < 1024)
r["memory"] = toHex(vm.memory());
else
r["sha3memory"] = sha3(vm.memory()).hex();
}
if (changesStorage(lastInst) || newContext)
{
Json::Value storage(Json::objectValue);
for (auto const& i: ext.state().storage(ext.myAddress))
storage[toHex(toCompactBigEndian(i.first), 1)] = toHex(toCompactBigEndian(i.second), 1);
r["storage"] = storage;
}
if (returned || newContext)
r["depth"] = ext.depth;
if (newContext)
r["address"] = ext.myAddress.hex();
r["steps"] = (unsigned)_steps;
r["inst"] = (unsigned)inst;
if (m_showMnemonics)
r["instname"] = instructionInfo(inst).name;
r["pc"] = toString(vm.curPC());
r["gas"] = toString(gas);
r["gascost"] = toString(gasCost);
if (!!newMemSize)
r["memexpand"] = toString(newMemSize);
m_trace->append(r);
}
string StandardTrace::json(bool _styled) const
{
return _styled ? Json::StyledWriter().write(*m_trace) : Json::FastWriter().write(*m_trace);
}
Executive::Executive(State& _s, BlockChain const& _bc, unsigned _level):
m_s(_s),
m_lastHashes(_bc.lastHashes((unsigned)_s.info().number - 1)),
@ -68,7 +176,7 @@ void Executive::initialize(Transaction const& _transaction)
if (!m_t.checkPayment())
{
clog(ExecutiveWarnChannel) << "Not enough gas to pay for the transaction: Require >" << m_t.gasRequired() << " Got" << m_t.gas();
m_excepted = TransactionException::OutOfGas;
m_excepted = TransactionException::OutOfGasBase;
BOOST_THROW_EXCEPTION(OutOfGasBase() << RequirementError(m_t.gasRequired(), (bigint)m_t.gas()));
}
@ -93,12 +201,12 @@ void Executive::initialize(Transaction const& _transaction)
// Avoid unaffordable transactions.
m_gasCost = (bigint)m_t.gas() * m_t.gasPrice();
m_totalCost = m_t.value() + m_gasCost;
if (m_s.balance(m_t.sender()) < m_totalCost)
bigint totalCost = m_t.value() + m_gasCost;
if (m_s.balance(m_t.sender()) < totalCost)
{
clog(ExecutiveWarnChannel) << "Not enough cash: Require >" << m_totalCost << " Got" << m_s.balance(m_t.sender()) << "for sender: " << m_t.sender();
clog(ExecutiveWarnChannel) << "Not enough cash: Require >" << totalCost << " Got" << m_s.balance(m_t.sender()) << "for sender: " << m_t.sender();
m_excepted = TransactionException::NotEnoughCash;
BOOST_THROW_EXCEPTION(NotEnoughCash() << RequirementError(m_totalCost, (bigint)m_s.balance(m_t.sender())) << errinfo_comment(m_t.sender().abridged()));
BOOST_THROW_EXCEPTION(NotEnoughCash() << RequirementError(totalCost, (bigint)m_s.balance(m_t.sender())) << errinfo_comment(m_t.sender().abridged()));
}
}
@ -151,7 +259,8 @@ bool Executive::call(CallParameters const& _p, u256 const& _gasPrice, Address co
{
m_outRef = _p.out; // Save ref to expected output buffer to be used in go()
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);
h256 codeHash = m_s.codeHash(_p.codeAddress);
m_ext = make_shared<ExtVM>(m_s, m_lastHashes, _p.receiveAddress, _p.senderAddress, _origin, _p.value, _gasPrice, _p.data, &c, codeHash, m_depth);
}
}
@ -171,7 +280,7 @@ bool Executive::create(Address _sender, u256 _endowment, u256 _gasPrice, u256 _g
// Execute _init.
if (!_init.empty())
m_ext = make_shared<ExtVM>(m_s, m_lastHashes, m_newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _init, m_depth);
m_ext = make_shared<ExtVM>(m_s, m_lastHashes, m_newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _init, sha3(_init), m_depth);
m_s.m_cache[m_newAddress] = Account(m_s.balance(m_newAddress), Account::ContractConception);
m_s.transferBalance(_sender, m_newAddress, _endowment);
@ -202,24 +311,6 @@ OnOpFunc Executive::simpleTrace()
};
}
OnOpFunc Executive::standardTrace(ostream& o_output)
{
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;
o_output << endl << " STACK" << endl;
for (auto i: vm.stack())
o_output << (h256)i << endl;
o_output << " MEMORY" << endl << ((vm.memory().size() > 1000) ? " mem size greater than 1000 bytes " : memDump(vm.memory()));
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 << gas << " : -" << dec << gasCost << " : " << newMemSize << "x32" << " >";
};
}
bool Executive::go(OnOpFunc const& _onOp)
{
if (m_ext)

27
libethereum/Executive.h

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

4
libethereum/ExtVM.h

@ -39,8 +39,8 @@ class ExtVM: public ExtVMFace
{
public:
/// Full constructor.
ExtVM(State& _s, LastHashes const& _lh, Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytesConstRef _code, unsigned _depth = 0):
ExtVMFace(_myAddress, _caller, _origin, _value, _gasPrice, _data, _code.toBytes(), _s.m_previousBlock, _s.m_currentBlock, _lh, _depth), m_s(_s), m_origCache(_s.m_cache)
ExtVM(State& _s, LastHashes const& _lh, Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytesConstRef _code, h256 const& _codeHash, unsigned _depth = 0):
ExtVMFace(_myAddress, _caller, _origin, _value, _gasPrice, _data, _code.toBytes(), _codeHash, _s.m_previousBlock, _s.m_currentBlock, _lh, _depth), m_s(_s), m_origCache(_s.m_cache)
{
m_s.ensureCached(_myAddress, true, true);
}

3
libethereum/Interface.h

@ -137,6 +137,7 @@ public:
virtual std::pair<h256, unsigned> transactionLocation(h256 const& _transactionHash) const = 0;
virtual h256 hashFromNumber(BlockNumber _number) const = 0;
virtual BlockNumber numberFromHash(h256 _blockHash) const = 0;
virtual int compareBlockHashes(h256 _h1, h256 _h2) const = 0;
virtual BlockInfo blockInfo(h256 _hash) const = 0;
virtual BlockDetails blockDetails(h256 _hash) const = 0;
@ -196,6 +197,8 @@ public:
virtual void stopMining() = 0;
/// Are we mining now?
virtual bool isMining() const = 0;
/// Would we like to mine now?
virtual bool wouldMine() const = 0;
/// Current hash rate.
virtual uint64_t hashrate() const = 0;

27
libethereum/LogFilter.cpp

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

15
libethereum/LogFilter.h

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

8
libethereum/Precompiled.cpp

@ -55,9 +55,13 @@ void ecrecoverCode(bytesConstRef _in, bytesRef _out)
{
try
{
ret = sha3(recover(sig, in.hash));
Public rec = recover(sig, in.hash);
if (rec)
ret = dev::sha3(rec);
else
return;
}
catch (...) {}
catch (...) { return; }
}
}

31
libethereum/Sentinel.h

@ -0,0 +1,31 @@
/**
* This file is generated by jsonrpcstub, DO NOT CHANGE IT MANUALLY!
*/
#ifndef JSONRPC_CPP_STUB_DEV_ETH_SENTINEL_H_
#define JSONRPC_CPP_STUB_DEV_ETH_SENTINEL_H_
#include <jsonrpccpp/client.h>
namespace dev {
namespace eth {
class Sentinel : public jsonrpc::Client
{
public:
Sentinel(jsonrpc::IClientConnector &conn, jsonrpc::clientVersion_t type = jsonrpc::JSONRPC_CLIENT_V2) : jsonrpc::Client(conn, type) {}
int eth_badBlock(const Json::Value& param1) throw (jsonrpc::JsonRpcException)
{
Json::Value p;
p.append(param1);
Json::Value result = this->CallMethod("eth_badBlock",p);
if (result.isInt())
return result.asInt();
else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
}
};
}
}
#endif //JSONRPC_CPP_STUB_DEV_ETH_SENTINEL_H_

3
libethereum/Sentinel.json

@ -0,0 +1,3 @@
[
{ "name": "eth_badBlock", "params": [ {} ], "order": [], "returns": 0 }
]

264
libethereum/State.cpp

@ -141,7 +141,7 @@ State::State(OverlayDB const& _db, BlockChain const& _bc, h256 _h, ImportRequire
// 2. Enact the block's transactions onto this state.
m_ourAddress = bi.coinbaseAddress;
enact(&b, _bc, _ir);
enact(BlockChain::verifyBlock(b), _bc, _ir);
}
else
{
@ -158,6 +158,7 @@ State::State(State const& _s):
m_transactions(_s.m_transactions),
m_receipts(_s.m_receipts),
m_transactionSet(_s.m_transactionSet),
m_touched(_s.m_touched),
m_cache(_s.m_cache),
m_previousBlock(_s.m_previousBlock),
m_currentBlock(_s.m_currentBlock),
@ -206,7 +207,7 @@ State::~State()
{
}
StateDiff State::diff(State const& _c) const
StateDiff State::diff(State const& _c, bool _quick) const
{
StateDiff ret;
@ -217,19 +218,29 @@ StateDiff State::diff(State const& _c) const
auto trie = SecureTrieDB<Address, OverlayDB>(const_cast<OverlayDB*>(&m_db), rootHash());
auto trieD = SecureTrieDB<Address, OverlayDB>(const_cast<OverlayDB*>(&_c.m_db), _c.rootHash());
for (auto i: trie)
ads.insert(i.first), trieAds.insert(i.first);
for (auto i: trieD)
ads.insert(i.first), trieAdsD.insert(i.first);
for (auto i: m_cache)
if (_quick)
{
trieAds = m_touched;
trieAdsD = _c.m_touched;
(ads += m_touched) += _c.m_touched;
}
else
{
for (auto const& i: trie)
ads.insert(i.first), trieAds.insert(i.first);
for (auto const& i: trieD)
ads.insert(i.first), trieAdsD.insert(i.first);
}
for (auto const& i: m_cache)
ads.insert(i.first);
for (auto i: _c.m_cache)
for (auto const& i: _c.m_cache)
ads.insert(i.first);
// cnote << *this;
// cnote << _c;
for (auto i: ads)
for (auto const& i: ads)
{
auto it = m_cache.find(i);
auto itD = _c.m_cache.find(i);
@ -272,7 +283,7 @@ void State::ensureCached(std::unordered_map<Address, Account>& _cache, Address _
void State::commit()
{
dev::eth::commit(m_cache, m_db, m_state);
m_touched += dev::eth::commit(m_cache, m_db, m_state);
m_cache.clear();
}
@ -382,7 +393,7 @@ bool State::sync(BlockChain const& _bc, h256 _block, BlockInfo const& _bi, Impor
return ret;
}
u256 State::enactOn(bytesConstRef _block, BlockInfo const& _bi, BlockChain const& _bc, ImportRequirements::value _ir)
u256 State::enactOn(VerifiedBlockRef const& _block, BlockChain const& _bc, ImportRequirements::value _ir)
{
#if ETH_TIMED_ENACTMENTS
boost::timer t;
@ -393,8 +404,8 @@ u256 State::enactOn(bytesConstRef _block, BlockInfo const& _bi, BlockChain const
#endif
// Check family:
BlockInfo biParent = _bc.info(_bi.parentHash);
_bi.verifyParent(biParent);
BlockInfo biParent = _bc.info(_block.info.parentHash);
_block.info.verifyParent(biParent);
#if ETH_TIMED_ENACTMENTS
populateVerify = t.elapsed();
@ -410,7 +421,7 @@ u256 State::enactOn(bytesConstRef _block, BlockInfo const& _bi, BlockChain const
t.restart();
#endif
sync(_bc, _bi.parentHash, BlockInfo(), _ir);
sync(_bc, _block.info.parentHash, BlockInfo(), _ir);
resetCurrent();
#if ETH_TIMED_ENACTMENTS
@ -450,6 +461,7 @@ void State::resetCurrent()
m_receipts.clear();
m_transactionSet.clear();
m_cache.clear();
m_touched.clear();
m_currentBlock = BlockInfo();
m_currentBlock.coinbaseAddress = m_ourAddress;
m_currentBlock.timestamp = max(m_previousBlock.timestamp + 1, (u256)time(0));
@ -577,101 +589,94 @@ string State::vmTrace(bytesConstRef _block, BlockChain const& _bc, ImportRequire
LastHashes lh = _bc.lastHashes((unsigned)m_previousBlock.number);
vector<bytes> receipts;
ostringstream ss;
string ret;
unsigned i = 0;
for (auto const& tr: rlp[1])
{
ss << " VM Execution of transaction" << i << ":" << endl;
execute(lh, Transaction(tr.data(), CheckTransaction::Everything), Permanence::Committed, Executive::standardTrace(ss));
StandardTrace st;
st.setShowMnemonics();
execute(lh, Transaction(tr.data(), CheckTransaction::Everything), Permanence::Committed, [&](uint64_t _steps, Instruction _inst, bigint _newMemSize, bigint _gasCost, bigint _gas, VM* _vm, ExtVMFace const* _extVM) { st(_steps, _inst, _newMemSize, _gasCost, _gas, _vm, _extVM); });
ret += (ret.empty() ? "[" : ",") + st.json();
RLPStream receiptRLP;
m_receipts.back().streamRLP(receiptRLP);
receipts.push_back(receiptRLP.out());
++i;
ss << endl;
}
return ss.str();
return ret.empty() ? "[]" : (ret + "]");
}
u256 State::enact(bytesConstRef _block, BlockChain const& _bc, ImportRequirements::value _ir)
u256 State::enact(VerifiedBlockRef const& _block, BlockChain const& _bc, ImportRequirements::value _ir)
{
// m_currentBlock is assumed to be prepopulated and reset.
BlockInfo bi(_block, (_ir & ImportRequirements::ValidNonce) ? CheckEverything : IgnoreNonce);
DEV_TIMED_FUNCTION_ABOVE(500);
// m_currentBlock is assumed to be prepopulated and reset.
#if !ETH_RELEASE
assert(m_previousBlock.hash() == bi.parentHash);
assert(m_currentBlock.parentHash == bi.parentHash);
assert(m_previousBlock.hash() == _block.info.parentHash);
assert(m_currentBlock.parentHash == _block.info.parentHash);
assert(rootHash() == m_previousBlock.stateRoot);
#endif
if (m_currentBlock.parentHash != m_previousBlock.hash())
// Internal client error.
BOOST_THROW_EXCEPTION(InvalidParentHash());
// Populate m_currentBlock with the correct values.
m_currentBlock = bi;
m_currentBlock.verifyInternals(_block);
m_currentBlock = _block.info;
m_currentBlock.noteDirty();
// cnote << "playback begins:" << m_state.root();
// cnote << m_state;
LastHashes lh = _bc.lastHashes((unsigned)m_previousBlock.number);
RLP rlp(_block);
LastHashes lh;
DEV_TIMED_ABOVE(lastHashes, 500)
lh = _bc.lastHashes((unsigned)m_previousBlock.number);
RLP rlp(_block.block);
vector<bytes> receipts;
// All ok with the block generally. Play back the transactions now...
unsigned i = 0;
for (auto const& tr: rlp[1])
{
try {
LogOverride<ExecutiveWarnChannel> o(false);
execute(lh, Transaction(tr.data(), CheckTransaction::Everything));
}
catch (...)
DEV_TIMED_ABOVE(txEcec, 500)
for (auto const& tr: _block.transactions)
{
badBlock(_block, "Invalid transaction");
cwarn << " Transaction Index:" << i;
LogOverride<ExecutiveWarnChannel> o(true);
execute(lh, Transaction(tr.data(), CheckTransaction::Everything));
throw;
try
{
LogOverride<ExecutiveWarnChannel> o(false);
execute(lh, tr);
}
catch (Exception& ex)
{
ex << errinfo_transactionIndex(i);
throw;
}
RLPStream receiptRLP;
m_receipts.back().streamRLP(receiptRLP);
receipts.push_back(receiptRLP.out());
++i;
}
RLPStream receiptRLP;
m_receipts.back().streamRLP(receiptRLP);
receipts.push_back(receiptRLP.out());
++i;
}
h256 receiptsRoot;
DEV_TIMED_ABOVE(receiptsRoot, 500)
receiptsRoot = orderedTrieRoot(receipts);
auto receiptsRoot = orderedTrieRoot(receipts);
if (receiptsRoot != m_currentBlock.receiptsRoot)
{
badBlock(_block, "Bad receipts state root");
cwarn << " Received: " << toString(m_currentBlock.receiptsRoot);
cwarn << " Expected: " << toString(receiptsRoot) << " which is:";
for (unsigned j = 0; j < i; ++j)
{
auto b = receipts[j];
cwarn << j << ": ";
cwarn << " RLP: " << RLP(b);
cwarn << " Hex: " << toHex(b);
cwarn << " " << TransactionReceipt(&b);
}
cwarn << " VMTrace:\n" << vmTrace(_block, _bc, _ir);
BOOST_THROW_EXCEPTION(InvalidReceiptsStateRoot());
InvalidReceiptsStateRoot ex;
ex << Hash256RequirementError(receiptsRoot, m_currentBlock.receiptsRoot);
ex << errinfo_receipts(receipts);
ex << errinfo_vmtrace(vmTrace(_block.block, _bc, _ir));
BOOST_THROW_EXCEPTION(ex);
}
if (m_currentBlock.logBloom != logBloom())
{
badBlock(_block, "Bad log bloom");
cwarn << " Receipt blooms:";
for (unsigned j = 0; j < i; ++j)
{
auto b = receipts[j];
cwarn << " " << j << ":" << TransactionReceipt(&b).bloom().hex();
}
cwarn << " Final bloom:" << m_currentBlock.logBloom.hex();
BOOST_THROW_EXCEPTION(InvalidLogBloom());
InvalidLogBloom ex;
ex << LogBloomRequirementError(logBloom(), m_currentBlock.logBloom);
ex << errinfo_receipts(receipts);
BOOST_THROW_EXCEPTION(ex);
}
// Initialise total difficulty calculation.
@ -680,72 +685,85 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, ImportRequirement
// Check uncles & apply their rewards to state.
if (rlp[2].itemCount() > 2)
{
badBlock(_block, "Too many uncles");
BOOST_THROW_EXCEPTION(TooManyUncles());
TooManyUncles ex;
ex << errinfo_max(2);
ex << errinfo_got(rlp[2].itemCount());
BOOST_THROW_EXCEPTION(ex);
}
vector<BlockInfo> rewarded;
h256Hash excluded = _bc.allKinFrom(m_currentBlock.parentHash, 6);
h256Hash excluded;
DEV_TIMED_ABOVE(allKin, 500)
excluded = _bc.allKinFrom(m_currentBlock.parentHash, 6);
excluded.insert(m_currentBlock.hash());
for (auto const& i: rlp[2])
{
auto h = sha3(i.data());
if (excluded.count(h))
unsigned ii = 0;
DEV_TIMED_ABOVE(uncleCheck, 500)
for (auto const& i: rlp[2])
{
badBlock(_block, "Invalid uncle included");
BOOST_THROW_EXCEPTION(UncleInChain() << errinfo_comment("Uncle in block already mentioned") << errinfo_data(toString(excluded)) << errinfo_hash256(sha3(i.data())));
}
excluded.insert(h);
try
{
auto h = sha3(i.data());
if (excluded.count(h))
{
UncleInChain ex;
ex << errinfo_comment("Uncle in block already mentioned");
ex << errinfo_unclesExcluded(excluded);
ex << errinfo_hash256(sha3(i.data()));
BOOST_THROW_EXCEPTION(ex);
}
excluded.insert(h);
BlockInfo uncle = BlockInfo::fromHeader(i.data(), (_ir & ImportRequirements::CheckUncles) ? CheckEverything : IgnoreNonce, h);
BlockInfo uncleParent(_bc.block(uncle.parentHash));
if ((bigint)uncleParent.number < (bigint)m_currentBlock.number - 7)
{
badBlock(_block, "Uncle too old");
cwarn << " Uncle number: " << uncle.number;
cwarn << " Uncle parent number: " << uncleParent.number;
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);
BlockInfo uncle = BlockInfo::fromHeader(i.data(), (_ir & ImportRequirements::CheckUncles) ? CheckEverything : IgnoreNonce, h);
// tdIncrease += uncle.difficulty;
rewarded.push_back(uncle);
}
BlockInfo uncleParent;
if (!_bc.isKnown(uncle.parentHash))
BOOST_THROW_EXCEPTION(UnknownParent());
uncleParent = BlockInfo(_bc.block(uncle.parentHash));
if ((bigint)uncleParent.number < (bigint)m_currentBlock.number - 7)
{
UncleTooOld ex;
ex << errinfo_uncleNumber(uncle.number);
ex << errinfo_currentNumber(m_currentBlock.number);
BOOST_THROW_EXCEPTION(ex);
}
else if (uncle.number == m_currentBlock.number)
{
UncleIsBrother ex;
ex << errinfo_uncleNumber(uncle.number);
ex << errinfo_currentNumber(m_currentBlock.number);
BOOST_THROW_EXCEPTION(ex);
}
uncle.verifyParent(uncleParent);
rewarded.push_back(uncle);
++ii;
}
catch (Exception& ex)
{
ex << errinfo_uncleIndex(ii);
throw;
}
}
applyRewards(rewarded);
DEV_TIMED_ABOVE(applyRewards, 500)
applyRewards(rewarded);
// Commit all cached state changes to the state trie.
commit();
DEV_TIMED_ABOVE(commit, 500)
commit();
// Hash the state trie and check against the state_root hash in m_currentBlock.
if (m_currentBlock.stateRoot != m_previousBlock.stateRoot && m_currentBlock.stateRoot != rootHash())
{
badBlock(_block, "Bad state root");
cnote << " Given to be:" << m_currentBlock.stateRoot;
// TODO: Fix
// cnote << SecureTrieDB<Address, OverlayDB>(&m_db, m_currentBlock.stateRoot);
cnote << " Calculated to be:" << rootHash();
cwarn << " VMTrace:\n" << vmTrace(_block, _bc, _ir);
// cnote << m_state;
// Rollback the trie.
m_db.rollback();
BOOST_THROW_EXCEPTION(InvalidStateRoot());
BOOST_THROW_EXCEPTION(InvalidStateRoot() << Hash256RequirementError(rootHash(), m_currentBlock.stateRoot));
}
if (m_currentBlock.gasUsed != gasUsed())
{
// Rollback the trie.
badBlock(_block, "Invalid gas used");
m_db.rollback();
BOOST_THROW_EXCEPTION(InvalidGasUsed() << RequirementError(bigint(gasUsed()), bigint(m_currentBlock.gasUsed)));
}
@ -760,7 +778,8 @@ void State::cleanup(bool _fullCommit)
paranoia("immediately before database commit", true);
// Commit the new trie to disk.
clog(StateTrace) << "Committing to disk: stateRoot" << m_currentBlock.stateRoot << "=" << rootHash() << "=" << toHex(asBytes(m_db.lookup(rootHash())));
if (isChannelVisible<StateTrace>()) // Avoid calling toHex if not needed
clog(StateTrace) << "Committing to disk: stateRoot" << m_currentBlock.stateRoot << "=" << rootHash() << "=" << toHex(asBytes(m_db.lookup(rootHash())));
try {
EnforceRefs er(m_db, true);
@ -773,7 +792,8 @@ void State::cleanup(bool _fullCommit)
}
m_db.commit();
clog(StateTrace) << "Committed: stateRoot" << m_currentBlock.stateRoot << "=" << rootHash() << "=" << toHex(asBytes(m_db.lookup(rootHash())));
if (isChannelVisible<StateTrace>()) // Avoid calling toHex if not needed
clog(StateTrace) << "Committed: stateRoot" << m_currentBlock.stateRoot << "=" << rootHash() << "=" << toHex(asBytes(m_db.lookup(rootHash())));
paranoia("immediately after database commit", true);
m_previousBlock = m_currentBlock;
@ -823,7 +843,7 @@ bool State::amIJustParanoid(BlockChain const& _bc)
cnote << "PARANOIA root:" << s.rootHash();
// s.m_currentBlock.populate(&block.out(), false);
// s.m_currentBlock.verifyInternals(&block.out());
s.enact(&block.out(), _bc, false); // don't check nonce for this since we haven't mined it yet.
s.enact(BlockChain::verifyBlock(block.out()), _bc, false); // don't check nonce for this since we haven't mined it yet.
s.cleanup(false);
return true;
}
@ -1122,6 +1142,8 @@ h256 State::codeHash(Address _contract) const
{
if (!addressHasCode(_contract))
return EmptySHA3;
if (m_cache[_contract].isFreshCode())
return sha3(code(_contract));
return m_cache[_contract].codeHash();
}
@ -1160,6 +1182,8 @@ bool State::isTrieGood(bool _enforceRefs, bool _requireNoLeftOvers) const
return true;
}
#define ETH_VMTIMER 1
ExecutionResult State::execute(LastHashes const& _lh, Transaction const& _t, Permanence _p, OnOpFunc const& _onOp)
{
#if ETH_PARANOIA
@ -1188,8 +1212,10 @@ ExecutionResult State::execute(LastHashes const& _lh, Transaction const& _t, Per
if (!e.execute())
#if ETH_VMTRACE
{
(void)_onOp;
e.go(e.simpleTrace());
if (isChannelVisible<VMTraceChannel>())
e.go(e.simpleTrace());
else
e.go(_onOp);
}
#else
e.go(_onOp);

33
libethereum/State.h

@ -46,8 +46,25 @@ namespace test { class ImportTest; class StateLoader; }
namespace eth
{
// Import-specific errinfos
using errinfo_uncleIndex = boost::error_info<struct tag_uncleIndex, unsigned>;
using errinfo_currentNumber = boost::error_info<struct tag_currentNumber, u256>;
using errinfo_uncleNumber = boost::error_info<struct tag_uncleNumber, u256>;
using errinfo_unclesExcluded = boost::error_info<struct tag_unclesExcluded, h256Hash>;
using errinfo_block = boost::error_info<struct tag_block, bytes>;
using errinfo_now = boost::error_info<struct tag_now, unsigned>;
using errinfo_transactionIndex = boost::error_info<struct tag_transactionIndex, unsigned>;
using errinfo_vmtrace = boost::error_info<struct tag_vmtrace, std::string>;
using errinfo_receipts = boost::error_info<struct tag_receipts, std::vector<bytes>>;
using errinfo_required_LogBloom = boost::error_info<struct tag_required_LogBloom, LogBloom>;
using errinfo_got_LogBloom = boost::error_info<struct tag_get_LogBloom, LogBloom>;
using LogBloomRequirementError = boost::tuple<errinfo_required_LogBloom, errinfo_got_LogBloom>;
class BlockChain;
class State;
struct VerifiedBlockRef;
struct StateChat: public LogChannel { static const char* name(); static const int verbosity = 4; };
struct StateTrace: public LogChannel { static const char* name(); static const int verbosity = 5; };
@ -294,10 +311,12 @@ public:
State fromPending(unsigned _i) const;
/// @returns the StateDiff caused by the pending transaction of index @a _i.
StateDiff pendingDiff(unsigned _i) const { return fromPending(_i).diff(fromPending(_i + 1)); }
StateDiff pendingDiff(unsigned _i) const { return fromPending(_i).diff(fromPending(_i + 1), true); }
/// @return the difference between this state (origin) and @a _c (destination).
StateDiff diff(State const& _c) const;
/// @param _quick if true doesn't check all addresses possible (/very/ slow for a full chain)
/// but rather only those touched by the transactions in creating the two States.
StateDiff diff(State const& _c, bool _quick = false) const;
/// Sync our state with the block chain.
/// This basically involves wiping ourselves if we've been superceded and rebuilding from the transaction queue.
@ -308,7 +327,7 @@ public:
/// Execute all transactions within a given block.
/// @returns the additional total difficulty.
u256 enactOn(bytesConstRef _block, BlockInfo const& _bi, BlockChain const& _bc, ImportRequirements::value _ir = ImportRequirements::Default);
u256 enactOn(VerifiedBlockRef const& _block, BlockChain const& _bc, ImportRequirements::value _ir = ImportRequirements::Default);
/// Returns back to a pristine state after having done a playback.
/// @arg _fullCommit if true flush everything out to disk. If false, this effectively only validates
@ -349,7 +368,7 @@ private:
/// Execute the given block, assuming it corresponds to m_currentBlock.
/// Throws on failure.
u256 enact(bytesConstRef _block, BlockChain const& _bc, ImportRequirements::value _ir = ImportRequirements::Default);
u256 enact(VerifiedBlockRef const& _block, BlockChain const& _bc, ImportRequirements::value _ir = ImportRequirements::Default);
/// Finalise the block, applying the earned rewards.
void applyRewards(std::vector<BlockInfo> const& _uncleBlockHeaders);
@ -371,6 +390,7 @@ private:
TransactionReceipts m_receipts; ///< The corresponding list of transaction receipts.
h256Hash m_transactionSet; ///< The set of transaction hashes that we've included in the state.
OverlayDB m_lastTx;
AddressHash m_touched; ///< Tracks all addresses touched by transactions so far.
mutable std::unordered_map<Address, Account> m_cache; ///< Our address cache. This stores the states of each address that has (or at least might have) been changed.
@ -394,8 +414,9 @@ private:
std::ostream& operator<<(std::ostream& _out, State const& _s);
template <class DB>
void commit(std::unordered_map<Address, Account> const& _cache, DB& _db, SecureTrieDB<Address, DB>& _state)
AddressHash commit(std::unordered_map<Address, Account> const& _cache, DB& _db, SecureTrieDB<Address, DB>& _state)
{
AddressHash ret;
for (auto const& i: _cache)
if (i.second.isDirty())
{
@ -434,7 +455,9 @@ void commit(std::unordered_map<Address, Account> const& _cache, DB& _db, SecureT
_state.insert(i.first, &s.out());
}
ret.insert(i.first);
}
return ret;
}
}

64
libethereum/Transaction.cpp

@ -39,29 +39,25 @@ 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)
TransactionException dev::eth::toTransactionException(Exception const& _e)
{
// Basic Transaction exceptions
if (!!dynamic_cast<BadRLP const*>(&_e))
return TransactionException::BadRLP;
if (!!dynamic_cast<OutOfGasIntrinsic const*>(&_e))
return TransactionException::OutOfGasIntrinsic;
if (!!dynamic_cast<InvalidSignature const*>(&_e))
return TransactionException::InvalidSignature;
// Executive exceptions
if (!!dynamic_cast<OutOfGasBase const*>(&_e))
return TransactionException::OutOfGasBase;
if (!!dynamic_cast<InvalidNonce const*>(&_e))
return TransactionException::InvalidNonce;
if (!!dynamic_cast<NotEnoughCash const*>(&_e))
return TransactionException::NotEnoughCash;
if (!!dynamic_cast<BlockGasLimitReached const*>(&_e))
return TransactionException::BlockGasLimitReached;
// VM execution exceptions
if (!!dynamic_cast<BadInstruction const*>(&_e))
return TransactionException::BadInstruction;
if (!!dynamic_cast<BadJumpDestination const*>(&_e))
@ -75,6 +71,28 @@ TransactionException dev::eth::toTransactionException(VMException const& _e)
return TransactionException::Unknown;
}
std::ostream& dev::eth::operator<<(std::ostream& _out, TransactionException const& _er)
{
switch (_er)
{
case TransactionException::None: _out << "None"; break;
case TransactionException::BadRLP: _out << "BadRLP"; break;
case TransactionException::OutOfGasIntrinsic: _out << "OutOfGasIntrinsic"; break;
case TransactionException::InvalidSignature: _out << "InvalidSignature"; break;
case TransactionException::InvalidNonce: _out << "InvalidNonce"; break;
case TransactionException::NotEnoughCash: _out << "NotEnoughCash"; break;
case TransactionException::OutOfGasBase: _out << "OutOfGasBase"; break;
case TransactionException::BlockGasLimitReached: _out << "BlockGasLimitReached"; break;
case TransactionException::BadInstruction: _out << "BadInstruction"; break;
case TransactionException::BadJumpDestination: _out << "BadJumpDestination"; break;
case TransactionException::OutOfGas: _out << "OutOfGas"; break;
case TransactionException::OutOfStack: _out << "OutOfStack"; break;
case TransactionException::StackUnderflow: _out << "StackUnderflow"; break;
default: _out << "Unknown"; break;
}
return _out;
}
Transaction::Transaction(bytesConstRef _rlpData, CheckTransaction _checkSig)
{
int field = 0;
@ -114,7 +132,7 @@ Transaction::Transaction(bytesConstRef _rlpData, CheckTransaction _checkSig)
throw;
}
if (_checkSig >= CheckTransaction::Cheap && !checkPayment())
BOOST_THROW_EXCEPTION(OutOfGasBase() << RequirementError(gasRequired(), (bigint)gas()));
BOOST_THROW_EXCEPTION(OutOfGasIntrinsic() << RequirementError(gasRequired(), (bigint)gas()));
}
Address const& Transaction::safeSender() const noexcept

9
libethereum/Transaction.h

@ -25,6 +25,7 @@
#include <libdevcore/SHA3.h>
#include <libethcore/Common.h>
#include <libevmcore/Params.h>
namespace dev
{
namespace eth
@ -48,6 +49,8 @@ enum class TransactionException
{
None = 0,
Unknown,
BadRLP,
OutOfGasIntrinsic, ///< Too little gas to pay for the base transaction cost.
InvalidSignature,
InvalidNonce,
NotEnoughCash,
@ -69,7 +72,8 @@ enum class CodeDeposit
struct VMException;
TransactionException toTransactionException(VMException const& _e);
TransactionException toTransactionException(Exception const& _e);
std::ostream& operator<<(std::ostream& _out, TransactionException const& _er);
/// Description of the result of executing a transaction.
struct ExecutionResult
@ -224,8 +228,5 @@ 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); }
}
}

53
libethereum/VerifiedBlock.h

@ -0,0 +1,53 @@
/*
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 VerfiedBlock.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include <libdevcore/Common.h>
#include <libethcore/BlockInfo.h>
#pragma once
namespace dev
{
namespace eth
{
class Transaction;
/// @brief Verified block info, does not hold block data, but a reference instead
struct VerifiedBlockRef
{
bytesConstRef block; ///< Block data reference
BlockInfo info; ///< Prepopulated block info
std::vector<Transaction> transactions; ///< Verified list of block transactions
};
/// @brief Verified block info, combines block data and verified info/transactions
struct VerifiedBlock
{
VerifiedBlockRef verified; ///< Verified block structures
bytes blockData; ///< Block data
};
using VerifiedBlocks = std::vector<VerifiedBlock>;
}
}

3
libevm/ExtVMFace.cpp

@ -25,7 +25,7 @@ using namespace std;
using namespace dev;
using namespace dev::eth;
ExtVMFace::ExtVMFace(Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytes const& _code, BlockInfo const& _previousBlock, BlockInfo const& _currentBlock, LastHashes const& _lh, unsigned _depth):
ExtVMFace::ExtVMFace(Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytes const& _code, h256 const& _codeHash, BlockInfo const& _previousBlock, BlockInfo const& _currentBlock, LastHashes const& _lh, unsigned _depth):
myAddress(_myAddress),
caller(_caller),
origin(_origin),
@ -33,6 +33,7 @@ ExtVMFace::ExtVMFace(Address _myAddress, Address _caller, Address _origin, u256
gasPrice(_gasPrice),
data(_data),
code(_code),
codeHash(_codeHash),
lastHashes(_lh),
previousBlock(_previousBlock),
currentBlock(_currentBlock),

29
libevm/ExtVMFace.h

@ -63,10 +63,28 @@ using LogEntries = std::vector<LogEntry>;
struct LocalisedLogEntry: public LogEntry
{
LocalisedLogEntry() {}
LocalisedLogEntry(LogEntry const& _le, unsigned _number, h256 _transactionHash = h256()): LogEntry(_le), number(_number), transactionHash(_transactionHash) {}
unsigned number = 0;
h256 transactionHash;
explicit LocalisedLogEntry(LogEntry const& _le): LogEntry(_le) {};
explicit LocalisedLogEntry(
LogEntry const& _le,
h256 _special
): LogEntry(_le), special(_special) {};
explicit LocalisedLogEntry(
LogEntry const& _le,
BlockInfo const& _bi,
h256 _th,
unsigned _ti,
unsigned _li
): LogEntry(_le), blockHash(_bi.hash()), blockNumber((BlockNumber)_bi.number), transactionHash(_th), transactionIndex(_ti), logIndex(_li), mined(true) {};
h256 blockHash = h256();
BlockNumber blockNumber = 0;
h256 transactionHash = h256();
unsigned transactionIndex = 0;
unsigned logIndex = 0;
bool mined = false;
h256 special = h256();
};
using LocalisedLogEntries = std::vector<LocalisedLogEntry>;
@ -130,7 +148,7 @@ public:
ExtVMFace() = default;
/// Full constructor.
ExtVMFace(Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytes const& _code, BlockInfo const& _previousBlock, BlockInfo const& _currentBlock, LastHashes const& _lh, unsigned _depth);
ExtVMFace(Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytes const& _code, h256 const& _codeHash, BlockInfo const& _previousBlock, BlockInfo const& _currentBlock, LastHashes const& _lh, unsigned _depth);
virtual ~ExtVMFace() = default;
@ -186,6 +204,7 @@ public:
u256 gasPrice; ///< Price of gas (that we already paid).
bytesConstRef data; ///< Current input data.
bytes code; ///< Current code that is executing.
h256 codeHash; ///< SHA3 hash of the executing code
LastHashes lastHashes; ///< Most recent 256 blocks' hashes.
BlockInfo previousBlock; ///< The previous block's information. TODO: PoC-8: REMOVE
BlockInfo currentBlock; ///< The current block's information.

322
libevm/VM.cpp

@ -45,165 +45,169 @@ static array<InstructionMetric, 256> metrics()
return s_ret;
}
bytesConstRef VM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp)
void VM::checkRequirements(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp, Instruction _inst)
{
// Reset leftovers from possible previous run
m_curPC = 0;
m_jumpDests.clear();
static const auto c_metrics = metrics();
auto& metric = c_metrics[static_cast<size_t>(_inst)];
m_stack.reserve((unsigned)c_stackLimit);
if (metric.gasPriceTier == InvalidTier)
BOOST_THROW_EXCEPTION(BadInstruction());
unique_ptr<CallParameters> callParams;
// FEES...
bigint runGas = c_tierStepGas[metric.gasPriceTier];
bigint newTempSize = m_temp.size();
bigint copySize = 0;
static const array<InstructionMetric, 256> c_metrics = metrics();
// should work, but just seems to result in immediate errorless exit on initial execution. yeah. weird.
//m_onFail = std::function<void()>(onOperation);
auto memNeed = [](u256 _offset, dev::u256 _size) { return _size ? (bigint)_offset + _size : (bigint)0; };
auto gasForMem = [](bigint _size) -> bigint
require(metric.args, metric.ret);
auto onOperation = [&]()
{
bigint s = _size / 32;
return (bigint)c_memoryGas * s + s * s / c_quadCoeffDiv;
if (_onOp)
_onOp(m_steps, _inst, newTempSize > m_temp.size() ? (newTempSize - m_temp.size()) / 32 : bigint(0), runGas, io_gas, this, &_ext);
};
if (m_jumpDests.empty())
for (unsigned i = 0; i < _ext.code.size(); ++i)
auto memNeed = [](u256 _offset, dev::u256 _size) { return _size ? (bigint)_offset + _size : (bigint)0; };
switch (_inst)
{
case Instruction::SSTORE:
if (!_ext.store(m_stack.back()) && m_stack[m_stack.size() - 2])
runGas = c_sstoreSetGas;
else if (_ext.store(m_stack.back()) && !m_stack[m_stack.size() - 2])
{
if (_ext.code[i] == (byte)Instruction::JUMPDEST)
m_jumpDests.push_back(i);
else if (_ext.code[i] >= (byte)Instruction::PUSH1 && _ext.code[i] <= (byte)Instruction::PUSH32)
i += _ext.code[i] - (unsigned)Instruction::PUSH1 + 1;
runGas = c_sstoreResetGas;
_ext.sub.refunds += c_sstoreRefundGas;
}
u256 nextPC = m_curPC + 1;
for (uint64_t steps = 0; true; m_curPC = nextPC, nextPC = m_curPC + 1, ++steps)
else
runGas = c_sstoreResetGas;
break;
case Instruction::SLOAD:
runGas = c_sloadGas;
break;
// These all operate on memory and therefore potentially expand it:
case Instruction::MSTORE:
newTempSize = (bigint)m_stack.back() + 32;
break;
case Instruction::MSTORE8:
newTempSize = (bigint)m_stack.back() + 1;
break;
case Instruction::MLOAD:
newTempSize = (bigint)m_stack.back() + 32;
break;
case Instruction::RETURN:
newTempSize = memNeed(m_stack.back(), m_stack[m_stack.size() - 2]);
break;
case Instruction::SHA3:
runGas = c_sha3Gas + (m_stack[m_stack.size() - 2] + 31) / 32 * c_sha3WordGas;
newTempSize = memNeed(m_stack.back(), m_stack[m_stack.size() - 2]);
break;
case Instruction::CALLDATACOPY:
copySize = m_stack[m_stack.size() - 3];
newTempSize = memNeed(m_stack.back(), m_stack[m_stack.size() - 3]);
break;
case Instruction::CODECOPY:
copySize = m_stack[m_stack.size() - 3];
newTempSize = memNeed(m_stack.back(), m_stack[m_stack.size() - 3]);
break;
case Instruction::EXTCODECOPY:
copySize = m_stack[m_stack.size() - 4];
newTempSize = memNeed(m_stack[m_stack.size() - 2], m_stack[m_stack.size() - 4]);
break;
case Instruction::JUMPDEST:
runGas = 1;
break;
case Instruction::LOG0:
case Instruction::LOG1:
case Instruction::LOG2:
case Instruction::LOG3:
case Instruction::LOG4:
{
// INSTRUCTION...
Instruction inst = (Instruction)_ext.getCode(m_curPC);
auto metric = c_metrics[(int)inst];
int gasPriceTier = metric.gasPriceTier;
if (gasPriceTier == InvalidTier)
BOOST_THROW_EXCEPTION(BadInstruction());
// FEES...
bigint runGas = c_tierStepGas[metric.gasPriceTier];
bigint newTempSize = m_temp.size();
bigint copySize = 0;
// should work, but just seems to result in immediate errorless exit on initial execution. yeah. weird.
//m_onFail = std::function<void()>(onOperation);
require(metric.args, metric.ret);
auto onOperation = [&]()
{
if (_onOp)
_onOp(steps, inst, newTempSize > m_temp.size() ? (newTempSize - m_temp.size()) / 32 : bigint(0), runGas, io_gas, this, &_ext);
};
switch (inst)
{
case Instruction::SSTORE:
if (!_ext.store(m_stack.back()) && m_stack[m_stack.size() - 2])
runGas = c_sstoreSetGas;
else if (_ext.store(m_stack.back()) && !m_stack[m_stack.size() - 2])
{
runGas = c_sstoreResetGas;
_ext.sub.refunds += c_sstoreRefundGas;
}
else
runGas = c_sstoreResetGas;
break;
unsigned n = (unsigned)_inst - (unsigned)Instruction::LOG0;
runGas = c_logGas + c_logTopicGas * n + (bigint)c_logDataGas * m_stack[m_stack.size() - 2];
newTempSize = memNeed(m_stack[m_stack.size() - 1], m_stack[m_stack.size() - 2]);
break;
}
case Instruction::SLOAD:
runGas = c_sloadGas;
break;
case Instruction::CALL:
case Instruction::CALLCODE:
runGas = (bigint)c_callGas + m_stack[m_stack.size() - 1];
if (_inst != Instruction::CALLCODE && !_ext.exists(asAddress(m_stack[m_stack.size() - 2])))
runGas += c_callNewAccountGas;
if (m_stack[m_stack.size() - 3] > 0)
runGas += c_callValueTransferGas;
newTempSize = std::max(memNeed(m_stack[m_stack.size() - 6], m_stack[m_stack.size() - 7]), memNeed(m_stack[m_stack.size() - 4], m_stack[m_stack.size() - 5]));
break;
case Instruction::CREATE:
{
newTempSize = memNeed(m_stack[m_stack.size() - 2], m_stack[m_stack.size() - 3]);
runGas = c_createGas;
break;
}
case Instruction::EXP:
{
auto expon = m_stack[m_stack.size() - 2];
runGas = c_expGas + c_expByteGas * (32 - (h256(expon).firstBitSet() / 8));
break;
}
default:;
}
// These all operate on memory and therefore potentially expand it:
case Instruction::MSTORE:
newTempSize = (bigint)m_stack.back() + 32;
break;
case Instruction::MSTORE8:
newTempSize = (bigint)m_stack.back() + 1;
break;
case Instruction::MLOAD:
newTempSize = (bigint)m_stack.back() + 32;
break;
case Instruction::RETURN:
newTempSize = memNeed(m_stack.back(), m_stack[m_stack.size() - 2]);
break;
case Instruction::SHA3:
runGas = c_sha3Gas + (m_stack[m_stack.size() - 2] + 31) / 32 * c_sha3WordGas;
newTempSize = memNeed(m_stack.back(), m_stack[m_stack.size() - 2]);
break;
case Instruction::CALLDATACOPY:
copySize = m_stack[m_stack.size() - 3];
newTempSize = memNeed(m_stack.back(), m_stack[m_stack.size() - 3]);
break;
case Instruction::CODECOPY:
copySize = m_stack[m_stack.size() - 3];
newTempSize = memNeed(m_stack.back(), m_stack[m_stack.size() - 3]);
break;
case Instruction::EXTCODECOPY:
copySize = m_stack[m_stack.size() - 4];
newTempSize = memNeed(m_stack[m_stack.size() - 2], m_stack[m_stack.size() - 4]);
break;
auto gasForMem = [](bigint _size) -> bigint
{
bigint s = _size / 32;
return (bigint)c_memoryGas * s + s * s / c_quadCoeffDiv;
};
case Instruction::JUMPDEST:
runGas = 1;
break;
newTempSize = (newTempSize + 31) / 32 * 32;
if (newTempSize > m_temp.size())
runGas += gasForMem(newTempSize) - gasForMem(m_temp.size());
runGas += c_copyGas * ((copySize + 31) / 32);
case Instruction::LOG0:
case Instruction::LOG1:
case Instruction::LOG2:
case Instruction::LOG3:
case Instruction::LOG4:
{
unsigned n = (unsigned)inst - (unsigned)Instruction::LOG0;
runGas = c_logGas + c_logTopicGas * n + (bigint)c_logDataGas * m_stack[m_stack.size() - 2];
newTempSize = memNeed(m_stack[m_stack.size() - 1], m_stack[m_stack.size() - 2]);
break;
}
onOperation();
case Instruction::CALL:
case Instruction::CALLCODE:
runGas = (bigint)c_callGas + m_stack[m_stack.size() - 1];
if (inst != Instruction::CALLCODE && !_ext.exists(asAddress(m_stack[m_stack.size() - 2])))
runGas += c_callNewAccountGas;
if (m_stack[m_stack.size() - 3] > 0)
runGas += c_callValueTransferGas;
newTempSize = std::max(memNeed(m_stack[m_stack.size() - 6], m_stack[m_stack.size() - 7]), memNeed(m_stack[m_stack.size() - 4], m_stack[m_stack.size() - 5]));
break;
if (io_gas < runGas)
BOOST_THROW_EXCEPTION(OutOfGas());
case Instruction::CREATE:
{
newTempSize = memNeed(m_stack[m_stack.size() - 2], m_stack[m_stack.size() - 3]);
runGas = c_createGas;
break;
}
case Instruction::EXP:
{
auto expon = m_stack[m_stack.size() - 2];
runGas = c_expGas + c_expByteGas * (32 - (h256(expon).firstBitSet() / 8));
break;
}
default:;
}
io_gas -= (u256)runGas;
newTempSize = (newTempSize + 31) / 32 * 32;
if (newTempSize > m_temp.size())
runGas += gasForMem(newTempSize) - gasForMem(m_temp.size());
runGas += c_copyGas * ((copySize + 31) / 32);
if (newTempSize > m_temp.size())
m_temp.resize((size_t)newTempSize);
}
onOperation();
bytesConstRef VM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp)
{
m_stack.reserve((unsigned)c_stackLimit);
if (io_gas < runGas)
BOOST_THROW_EXCEPTION(OutOfGas());
for (size_t i = 0; i < _ext.code.size(); ++i)
{
if (_ext.code[i] == (byte)Instruction::JUMPDEST)
m_jumpDests.push_back(i);
else if (_ext.code[i] >= (byte)Instruction::PUSH1 && _ext.code[i] <= (byte)Instruction::PUSH32)
i += _ext.code[i] - (size_t)Instruction::PUSH1 + 1;
}
io_gas -= (u256)runGas;
auto verifyJumpDest = [](u256 const& _dest, std::vector<uint64_t> const& _validDests)
{
auto nextPC = static_cast<uint64_t>(_dest);
if (!std::binary_search(_validDests.begin(), _validDests.end(), nextPC) || _dest > std::numeric_limits<uint64_t>::max())
BOOST_THROW_EXCEPTION(BadJumpDestination());
return nextPC;
};
if (newTempSize > m_temp.size())
m_temp.resize((size_t)newTempSize);
m_steps = 0;
for (auto nextPC = m_curPC + 1; true; m_curPC = nextPC, nextPC = m_curPC + 1, ++m_steps)
{
Instruction inst = (Instruction)_ext.getCode(m_curPC);
checkRequirements(io_gas, _ext, _onOp, inst);
// EXECUTE...
switch (inst)
{
case Instruction::ADD:
@ -299,7 +303,7 @@ bytesConstRef VM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp)
case Instruction::SIGNEXTEND:
if (m_stack.back() < 31)
{
unsigned const testBit(m_stack.back() * 8 + 7);
auto testBit = static_cast<unsigned>(m_stack.back()) * 8 + 7;
u256& number = m_stack[m_stack.size() - 2];
u256 mask = ((u256(1) << testBit) - 1);
if (boost::multiprecision::bit_test(number, testBit))
@ -480,7 +484,7 @@ bytesConstRef VM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp)
case Instruction::DUP15:
case Instruction::DUP16:
{
auto n = 1 + (int)inst - (int)Instruction::DUP1;
auto n = 1 + (unsigned)inst - (unsigned)Instruction::DUP1;
m_stack.push_back(m_stack[m_stack.size() - n]);
break;
}
@ -501,7 +505,7 @@ bytesConstRef VM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp)
case Instruction::SWAP15:
case Instruction::SWAP16:
{
unsigned n = (int)inst - (int)Instruction::SWAP1 + 2;
auto n = (unsigned)inst - (unsigned)Instruction::SWAP1 + 2;
auto d = m_stack.back();
m_stack.back() = m_stack[m_stack.size() - n];
m_stack[m_stack.size() - n] = d;
@ -535,18 +539,12 @@ bytesConstRef VM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp)
m_stack.pop_back();
break;
case Instruction::JUMP:
nextPC = m_stack.back();
if (find(m_jumpDests.begin(), m_jumpDests.end(), (uint64_t)nextPC) == m_jumpDests.end() || nextPC > numeric_limits<uint64_t>::max() )
BOOST_THROW_EXCEPTION(BadJumpDestination());
nextPC = verifyJumpDest(m_stack.back(), m_jumpDests);
m_stack.pop_back();
break;
case Instruction::JUMPI:
if (m_stack[m_stack.size() - 2])
{
nextPC = m_stack.back();
if (find(m_jumpDests.begin(), m_jumpDests.end(), (uint64_t)nextPC) == m_jumpDests.end() || nextPC > numeric_limits<uint64_t>::max() )
BOOST_THROW_EXCEPTION(BadJumpDestination());
}
nextPC = verifyJumpDest(m_stack.back(), m_jumpDests);
m_stack.pop_back();
m_stack.pop_back();
break;
@ -598,7 +596,7 @@ bytesConstRef VM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp)
break;
case Instruction::CREATE:
{
u256 endowment = m_stack.back();
auto endowment = m_stack.back();
m_stack.pop_back();
unsigned initOff = (unsigned)m_stack.back();
m_stack.pop_back();
@ -614,16 +612,14 @@ bytesConstRef VM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp)
case Instruction::CALL:
case Instruction::CALLCODE:
{
if (!callParams)
callParams.reset(new CallParameters);
callParams->gas = m_stack.back();
CallParameters callParams;
callParams.gas = m_stack.back();
if (m_stack[m_stack.size() - 3] > 0)
callParams->gas += c_callStipend;
callParams.gas += c_callStipend;
m_stack.pop_back();
callParams->codeAddress = asAddress(m_stack.back());
callParams.codeAddress = asAddress(m_stack.back());
m_stack.pop_back();
callParams->value = m_stack.back();
callParams.value = m_stack.back();
m_stack.pop_back();
unsigned inOff = (unsigned)m_stack.back();
@ -635,19 +631,19 @@ bytesConstRef VM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp)
unsigned outSize = (unsigned)m_stack.back();
m_stack.pop_back();
if (_ext.balance(_ext.myAddress) >= callParams->value && _ext.depth < 1024)
if (_ext.balance(_ext.myAddress) >= callParams.value && _ext.depth < 1024)
{
callParams->onOp = _onOp;
callParams->senderAddress = _ext.myAddress;
callParams->receiveAddress = inst == Instruction::CALL ? callParams->codeAddress : callParams->senderAddress;
callParams->data = bytesConstRef(m_temp.data() + inOff, inSize);
callParams->out = bytesRef(m_temp.data() + outOff, outSize);
m_stack.push_back(_ext.call(*callParams));
callParams.onOp = _onOp;
callParams.senderAddress = _ext.myAddress;
callParams.receiveAddress = inst == Instruction::CALL ? callParams.codeAddress : callParams.senderAddress;
callParams.data = bytesConstRef(m_temp.data() + inOff, inSize);
callParams.out = bytesRef(m_temp.data() + outOff, outSize);
m_stack.push_back(_ext.call(callParams));
}
else
m_stack.push_back(0);
io_gas += callParams->gas;
io_gas += callParams.gas;
break;
}
case Instruction::RETURN:

6
libevm/VM.h

@ -54,16 +54,18 @@ class VM: public VMFace
public:
virtual bytesConstRef execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp) override final;
u256 curPC() const { return m_curPC; }
uint64_t curPC() const { return m_curPC; }
bytes const& memory() const { return m_temp; }
u256s const& stack() const { return m_stack; }
private:
void checkRequirements(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp, Instruction _inst);
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;
uint64_t m_curPC = 0;
uint64_t m_steps = 0;
bytes m_temp;
u256s m_stack;
std::vector<uint64_t> m_jumpDests;

13
libevm/VMFace.h

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

2
libevmasm/Assembly.cpp

@ -109,7 +109,7 @@ string Assembly::getLocationFromSources(StringMap const& _sourceCodes, SourceLoc
if (newLinePos != string::npos)
cut = cut.substr(0, newLinePos) + "...";
return move(cut);
return cut;
}
ostream& Assembly::streamAsm(ostream& _out, string const& _prefix, StringMap const& _sourceCodes) const

21
libevmasm/CommonSubexpressionEliminator.cpp

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

4
libevmcore/Exceptions.h

@ -28,8 +28,8 @@ namespace dev
namespace eth
{
struct InvalidDeposit: virtual Exception {};
struct InvalidOpcode: virtual Exception {};
DEV_SIMPLE_EXCEPTION(InvalidDeposit);
DEV_SIMPLE_EXCEPTION(InvalidOpcode);
}
}

2
libjsqrc/ethereumjs/.jshintrc

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

7
libjsqrc/ethereumjs/.versions

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

5
libjsqrc/ethereumjs/bower.json

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

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

File diff suppressed because one or more lines are too long

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

File diff suppressed because one or more lines are too long

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

File diff suppressed because one or more lines are too long

1917
libjsqrc/ethereumjs/dist/web3.js

File diff suppressed because one or more lines are too long

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

File diff suppressed because one or more lines are too long

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

File diff suppressed because one or more lines are too long

23
libjsqrc/ethereumjs/example/contract.html

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

27
libjsqrc/ethereumjs/example/event_inc.html

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

203
libjsqrc/ethereumjs/example/icap.html

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

102
libjsqrc/ethereumjs/example/namereg.html

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

76
libjsqrc/ethereumjs/example/natspec_contract.html

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

2
libjsqrc/ethereumjs/index.js

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

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

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

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

@ -36,26 +36,34 @@
/// required to define ETH_BIGNUMBER_ROUNDING_MODE
var BigNumber = require('bignumber.js');
var ETH_UNITS = [
'wei',
'Kwei',
'Mwei',
'Gwei',
'szabo',
'finney',
'ether',
'grand',
'Mether',
'Gether',
'Tether',
'Pether',
'Eether',
'Zether',
'Yether',
'Nether',
'Dether',
'Vether',
'Uether'
var ETH_UNITS = [
'wei',
'kwei',
'Mwei',
'Gwei',
'szabo',
'finney',
'femtoether',
'picoether',
'nanoether',
'microether',
'milliether',
'nano',
'micro',
'milli',
'ether',
'grand',
'Mether',
'Gether',
'Tether',
'Pether',
'Eether',
'Zether',
'Yether',
'Nether',
'Dether',
'Vether',
'Uether'
];
module.exports = {

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

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

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

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

2
libjsqrc/ethereumjs/lib/version.json

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

13
libjsqrc/ethereumjs/lib/web3.js

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

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

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

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

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

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

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

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

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

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

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

2
libjsqrc/ethereumjs/package.js

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

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save