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. 1877
      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. 1879
      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. 29
      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. 10
      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(rlp)
add_subdirectory(abi) add_subdirectory(abi)
add_subdirectory(ethvm)
add_subdirectory(eth) add_subdirectory(eth)
if("x${CMAKE_BUILD_TYPE}" STREQUAL "xDebug") if("x${CMAKE_BUILD_TYPE}" STREQUAL "xDebug")

9
alethzero/MainWin.cpp

@ -240,6 +240,10 @@ Main::Main(QWidget *parent) :
#if !ETH_FATDB #if !ETH_FATDB
removeDockWidget(ui->dockWidget_accounts); removeDockWidget(ui->dockWidget_accounts);
#endif
#if !ETH_EVMJIT
ui->jitvm->setEnabled(false);
ui->jitvm->setChecked(false);
#endif #endif
installWatches(); installWatches();
startTimer(100); startTimer(100);
@ -805,6 +809,7 @@ void Main::readSettings(bool _skipGeometry)
ui->usePrivate->setChecked(m_privateChain.size()); ui->usePrivate->setChecked(m_privateChain.size());
ui->verbosity->setValue(s.value("verbosity", 1).toInt()); ui->verbosity->setValue(s.value("verbosity", 1).toInt());
ui->jitvm->setChecked(s.value("jitvm", true).toBool()); 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 ui->urlEdit->setText(s.value("url", "about:blank").toString()); //http://gavwood.com/gavcoin.html
on_urlEdit_returnPressed(); on_urlEdit_returnPressed();
@ -1240,7 +1245,9 @@ void Main::refreshBlockCount()
{ {
auto d = ethereum()->blockChain().details(); auto d = ethereum()->blockChain().details();
BlockQueueStatus b = ethereum()->blockQueueStatus(); 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() void Main::on_turboMining_triggered()

2
alethzero/MainWin.h

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

6
alethzero/NatspecHandler.h

@ -39,17 +39,17 @@ class NatspecHandler: public NatSpecFace
~NatspecHandler(); ~NatspecHandler();
/// Stores locally in a levelDB a key value pair of contract code hash to natspec documentation /// 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 /// Retrieves the natspec documentation as a string given a contract code hash
std::string retrieve(dev::h256 const& _contractHash) const override; std::string retrieve(dev::h256 const& _contractHash) const override;
/// Given a json natspec string and the transaction data return the user notice /// 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 /// Given a contract code hash and the transaction's data retrieve the natspec documention's
/// user notice for that transaction. /// user notice for that transaction.
/// @returns The user notice or an empty string if no natspec for the contract exists /// @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 /// 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: private:
ldb::ReadOptions m_readOptions; 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_DEBUG "-O0 -g -DETH_DEBUG")
set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG -DETH_RELEASE") set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG -DETH_RELEASE")
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -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") if ("${CMAKE_SYSTEM_NAME}" MATCHES "Linux")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libstdc++ -fcolor-diagnostics -Qunused-arguments -DBOOST_ASIO_HAS_CLANG_LIBCXX") 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 << " --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 << " --master <password> Give the master password for the key store." << endl
<< " --password <password> Give a password for a private key." << 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 << endl
<< "Client transacting:" << endl << "Client transacting:" << endl
/*<< " -B,--block-fees <n> Set the block fee profit in the reference unit e.g. ¢ (default: 15)." << 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 << " -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 << " -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 << " -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 << " -C,--cpu When mining, use the CPU." << endl
<< " -G,--opencl When mining use the GPU via OpenCL." << 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 << " --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; bool upnp = true;
WithExisting killChain = WithExisting::Trust; WithExisting killChain = WithExisting::Trust;
bool jit = false; bool jit = false;
string sentinel;
/// Networking params. /// Networking params.
string clientName; string clientName;
@ -303,6 +306,7 @@ int main(int argc, char** argv)
/// Mining params /// Mining params
unsigned mining = 0; unsigned mining = 0;
bool forceMining = false; bool forceMining = false;
bool mineOnWrongChain = false;
Address signingKey; Address signingKey;
Address sessionKey; Address sessionKey;
Address beneficiary = signingKey; Address beneficiary = signingKey;
@ -385,6 +389,10 @@ int main(int argc, char** argv)
mode = OperationMode::Export; mode = OperationMode::Export;
filename = argv[++i]; 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) else if (arg == "--format" && i + 1 < argc)
{ {
string m = argv[++i]; string m = argv[++i];
@ -680,6 +688,8 @@ int main(int argc, char** argv)
nodeMode == NodeMode::Full ? set<string>{"eth"/*, "shh"*/} : set<string>(), nodeMode == NodeMode::Full ? set<string>{"eth"/*, "shh"*/} : set<string>(),
netPrefs, netPrefs,
&nodesState); &nodesState);
web3.ethereum()->setMineOnBadChain(mineOnWrongChain);
web3.ethereum()->setSentinel(sentinel);
auto toNumber = [&](string const& s) -> unsigned { auto toNumber = [&](string const& s) -> unsigned {
if (s == "latest") if (s == "latest")

3
ethminer/CMakeLists.txt

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

9
ethminer/MinerAux.h

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

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.timestamp = static_cast<decltype(m_data.timestamp)>(_ext.currentBlock.timestamp);
m_data.code = _ext.code.data(); m_data.code = _ext.code.data();
m_data.codeSize = _ext.code.size(); 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 env = reinterpret_cast<Env*>(&_ext);
auto exitCode = m_engine.run(&m_data, env); auto exitCode = m_engine.run(&m_data, env);

43
evmjit/libevmjit/Array.cpp

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

27
evmjit/libevmjit/Cache.cpp

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

5
json_spirit/json_spirit_writer_template.h

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

2
libdevcore/Common.cpp

@ -28,7 +28,7 @@ using namespace dev;
namespace dev namespace dev
{ {
char const* Version = "0.9.24"; char const* Version = "0.9.25";
const u256 UndefinedU256 = ~(u256)0; 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. /// Scope guard for invariant check in a class derived from HasInvariants.
#if ETH_DEBUG #if ETH_DEBUG
#define DEV_INVARIANT_CHECK ::dev::InvariantChecker __dev_invariantCheck(this) #define DEV_INVARIANT_CHECK { ::dev::InvariantChecker __dev_invariantCheck(this); }
#else #else
#define DEV_INVARIANT_CHECK (void)0; #define DEV_INVARIANT_CHECK (void)0;
#endif #endif

39
libdevcore/Exceptions.h

@ -30,7 +30,8 @@
namespace dev namespace dev
{ {
// base class for all exceptions
/// Base class for all exceptions.
struct Exception: virtual std::exception, virtual boost::exception struct Exception: virtual std::exception, virtual boost::exception
{ {
Exception(std::string _message = std::string()): m_message(std::move(_message)) {} Exception(std::string _message = std::string()): m_message(std::move(_message)) {}
@ -40,20 +41,26 @@ private:
std::string m_message; std::string m_message;
}; };
struct BadHexCharacter: virtual Exception {}; #define DEV_SIMPLE_EXCEPTION(X) struct X: virtual Exception { const char* what() const noexcept override { return #X; } }
struct RLPException: virtual Exception {};
struct BadCast: virtual RLPException {}; /// Base class for all RLP exceptions.
struct BadRLP: virtual RLPException {}; struct RLPException: virtual Exception { RLPException(std::string _message = std::string()): Exception(_message) {} };
struct OversizeRLP: virtual RLPException {}; #define DEV_SIMPLE_EXCEPTION_RLP(X) struct X: virtual RLPException { const char* what() const noexcept override { return #X; } }
struct UndersizeRLP: virtual RLPException {};
struct NoNetworking: virtual Exception {}; DEV_SIMPLE_EXCEPTION_RLP(BadCast);
struct NoUPnPDevice: virtual Exception {}; DEV_SIMPLE_EXCEPTION_RLP(BadRLP);
struct RootNotFound: virtual Exception {}; DEV_SIMPLE_EXCEPTION_RLP(OversizeRLP);
struct BadRoot: virtual Exception {}; DEV_SIMPLE_EXCEPTION_RLP(UndersizeRLP);
struct FileError: virtual Exception {};
struct Overflow: virtual Exception {}; 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 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.") {} }; struct ExternalFunctionFailure: virtual Exception { public: ExternalFunctionFailure(std::string _f): Exception("Function " + _f + "() failed.") {} };
// error information to be added to exceptions // 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 errinfo_max = boost::error_info<struct tag_max, bigint>;
using RequirementError = boost::tuple<errinfo_required, errinfo_got>; using RequirementError = boost::tuple<errinfo_required, errinfo_got>;
using errinfo_hash256 = boost::error_info<struct tag_hash, h256>; 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). /// or equal to the currently output verbosity (g_logVerbosity).
static map<type_info const*, bool> s_logOverride; static map<type_info const*, bool> s_logOverride;
bool 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): LogOverrideAux::LogOverrideAux(std::type_info const* _ch, bool _value):
m_ch(_ch) m_ch(_ch)
{ {

3
libdevcore/Log.h

@ -73,6 +73,9 @@ public:
LogOverride(bool _value): LogOverrideAux(&typeid(Channel), _value) {} 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. /// Temporary changes system's verbosity for specific function. Restores the old verbosity when function returns.
/// Not thread-safe, use with caution! /// Not thread-safe, use with caution!
struct VerbosityHolder 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 std::unordered_map<h256, std::string> MemoryDB::get() const
{ {
#if DEV_GUARDED_DB
ReadGuard l(x_this); ReadGuard l(x_this);
#endif
std::unordered_map<h256, std::string> ret; std::unordered_map<h256, std::string> ret;
for (auto const& i: m_main) for (auto const& i: m_main)
if (!m_enforceRefs || i.second.second > 0) if (!m_enforceRefs || i.second.second > 0)
@ -44,8 +46,10 @@ MemoryDB& MemoryDB::operator=(MemoryDB const& _c)
{ {
if (this == &_c) if (this == &_c)
return *this; return *this;
#if DEV_GUARDED_DB
ReadGuard l(_c.x_this); ReadGuard l(_c.x_this);
WriteGuard l2(x_this); WriteGuard l2(x_this);
#endif
m_main = _c.m_main; m_main = _c.m_main;
m_aux = _c.m_aux; m_aux = _c.m_aux;
return *this; return *this;
@ -53,7 +57,9 @@ MemoryDB& MemoryDB::operator=(MemoryDB const& _c)
std::string MemoryDB::lookup(h256 const& _h) const std::string MemoryDB::lookup(h256 const& _h) const
{ {
#if DEV_GUARDED_DB
ReadGuard l(x_this); ReadGuard l(x_this);
#endif
auto it = m_main.find(_h); auto it = m_main.find(_h);
if (it != m_main.end()) if (it != m_main.end())
{ {
@ -67,7 +73,9 @@ std::string MemoryDB::lookup(h256 const& _h) const
bool MemoryDB::exists(h256 const& _h) const bool MemoryDB::exists(h256 const& _h) const
{ {
#if DEV_GUARDED_DB
ReadGuard l(x_this); ReadGuard l(x_this);
#endif
auto it = m_main.find(_h); auto it = m_main.find(_h);
if (it != m_main.end() && (!m_enforceRefs || it->second.second > 0)) if (it != m_main.end() && (!m_enforceRefs || it->second.second > 0))
return true; return true;
@ -76,7 +84,9 @@ bool MemoryDB::exists(h256 const& _h) const
void MemoryDB::insert(h256 const& _h, bytesConstRef _v) void MemoryDB::insert(h256 const& _h, bytesConstRef _v)
{ {
#if DEV_GUARDED_DB
WriteGuard l(x_this); WriteGuard l(x_this);
#endif
auto it = m_main.find(_h); auto it = m_main.find(_h);
if (it != m_main.end()) if (it != m_main.end())
{ {
@ -92,7 +102,9 @@ void MemoryDB::insert(h256 const& _h, bytesConstRef _v)
bool MemoryDB::kill(h256 const& _h) bool MemoryDB::kill(h256 const& _h)
{ {
#if DEV_GUARDED_DB
ReadGuard l(x_this); ReadGuard l(x_this);
#endif
if (m_main.count(_h)) if (m_main.count(_h))
{ {
if (m_main[_h].second > 0) if (m_main[_h].second > 0)
@ -117,9 +129,38 @@ bool MemoryDB::kill(h256 const& _h)
return false; 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() void MemoryDB::purge()
{ {
#if DEV_GUARDED_DB
WriteGuard l(x_this); WriteGuard l(x_this);
#endif
for (auto it = m_main.begin(); it != m_main.end(); ) for (auto it = m_main.begin(); it != m_main.end(); )
if (it->second.second) if (it->second.second)
++it; ++it;
@ -129,7 +170,9 @@ void MemoryDB::purge()
h256Hash MemoryDB::keys() const h256Hash MemoryDB::keys() const
{ {
#if DEV_GUARDED_DB
ReadGuard l(x_this); ReadGuard l(x_this);
#endif
h256Hash ret; h256Hash ret;
for (auto const& i: m_main) for (auto const& i: m_main)
if (i.second.second) if (i.second.second)

8
libdevcore/MemoryDB.h

@ -57,14 +57,16 @@ public:
bool kill(h256 const& _h); bool kill(h256 const& _h);
void purge(); 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(); } bytes lookupAux(h256 const& _h) const;
void removeAux(h256 const& _h) { WriteGuard l(x_this); m_aux[_h].second = false; } void removeAux(h256 const& _h);
void insertAux(h256 const& _h, bytesConstRef _v) { WriteGuard l(x_this); m_aux[_h] = make_pair(_v.toBytes(), true); } void insertAux(h256 const& _h, bytesConstRef _v);
h256Hash keys() const; h256Hash keys() const;
protected: protected:
#if DEV_GUARDED_DB
mutable SharedMutex x_this; mutable SharedMutex x_this;
#endif
std::unordered_map<h256, std::pair<std::string, unsigned>> m_main; std::unordered_map<h256, std::pair<std::string, unsigned>> m_main;
std::unordered_map<h256, std::pair<bytes, bool>> m_aux; 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; 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: private:
UnsignedRange m_all; UnsignedRange m_all;
std::map<T, T> m_ranges; 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>(); } 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(_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 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); } 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; } _T* begin() { return m_data; }

4
libdevcrypto/Common.cpp

@ -54,7 +54,7 @@ Public dev::toPublic(Secret const& _secret)
{ {
Public p; Public p;
s_secp256k1.toPublic(_secret, p); s_secp256k1.toPublic(_secret, p);
return std::move(p); return p;
} }
Address dev::toAddress(Public const& _public) Address dev::toAddress(Public const& _public)
@ -230,7 +230,7 @@ h256 crypto::kdf(Secret const& _priv, h256 const& _hash)
if (!s || !_hash || !_priv) if (!s || !_hash || !_priv)
BOOST_THROW_EXCEPTION(InvalidState()); BOOST_THROW_EXCEPTION(InvalidState());
return std::move(s); return s;
} }
h256 Nonce::get(bool _commit) 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); k.resize(kdByteLen);
return move(k); return k;
} }
void Secp256k1::encryptECIES(Public const& _k, bytes& io_cipher) void Secp256k1::encryptECIES(Public const& _k, bytes& io_cipher)
@ -264,7 +264,6 @@ Public Secp256k1::recover(Signature _signature, bytesConstRef _message)
ECP::Element x; ECP::Element x;
{ {
Guard l(x_curve);
m_curve.DecodePoint(x, encodedpoint, 33); m_curve.DecodePoint(x, encodedpoint, 33);
if (!m_curve.VerifyPoint(x)) if (!m_curve.VerifyPoint(x))
return recovered; return recovered;
@ -286,7 +285,6 @@ Public Secp256k1::recover(Signature _signature, bytesConstRef _message)
ECP::Point p; ECP::Point p;
byte recoveredbytes[65]; byte recoveredbytes[65];
{ {
Guard l(x_curve);
// todo: make generator member // todo: make generator member
p = m_curve.CascadeMultiply(u2, x, u1, m_params.GetSubgroupGenerator()); p = m_curve.CascadeMultiply(u2, x, u1, m_params.GetSubgroupGenerator());
m_curve.EncodePoint(recoveredbytes, p, false); m_curve.EncodePoint(recoveredbytes, p, false);

2
libdevcrypto/CryptoPP.h

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

17
libethash-cl/ethash_cl_miner.h

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

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); mixHash = _header[field = 13].toHash<h256>(RLP::VeryStrict);
nonce = _header[field = 14].toHash<Nonce>(RLP::VeryStrict); nonce = _header[field = 14].toHash<Nonce>(RLP::VeryStrict);
} }
catch (Exception const& _e) catch (Exception const& _e)
{ {
_e << errinfo_name("invalid block header format") << BadFieldError(field, toHex(_header[field].data().toBytes())); _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. // check it hashes according to proof of work or that it's the genesis block.
if (_s == CheckEverything && parentHash && !ProofOfWork::verify(*this)) 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)) 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) if (_s != CheckNothing)
{ {
@ -224,7 +240,7 @@ void BlockInfo::verifyInternals(bytesConstRef _block) const
for (auto const& t: txs) for (auto const& t: txs)
cdebug << toHex(t); 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())); clog(BlockInfoDiagnosticsChannel) << "Expected uncle hash:" << toString(sha3(root[2].data()));
if (sha3Uncles != 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) static void badBlockInfo(BlockInfo const& _bi, string const& _err)
{ {
cwarn << EthRedBold << "========================================================================"; string const c_line = EthReset EthOnMaroon + string(80, ' ');
cwarn << EthRedBold << "== Software Failure " + _err + string(max<int>(0, 44 - _err.size()), ' ') + " =="; 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); string bin = toString(_bi.number);
cwarn << EthRedBold << ("== Guru Meditation #" + string(max<int>(0, 8 - bin.size()), '0') + bin + "." + _bi.hash().abridged() + " =="); ss << c_border + (" Guru Meditation #" + string(max<int>(0, 8 - bin.size()), '0') + bin + "." + _bi.hash().abridged() + " ") + c_border << endl;
cwarn << EthRedBold << "========================================================================"; ss << c_space << endl;
ss << c_line;
cwarn << "\n" + ss.str();
} }
void badBlock(bytesConstRef _block, string const& _err) void badBlock(bytesConstRef _block, string const& _err)
{ {
badBlockInfo(BlockInfo(_block, CheckNothing), _err); BlockInfo bi;
cwarn << " Block:" << toHex(_block); DEV_IGNORE_EXCEPTIONS(bi = BlockInfo(_block, CheckNothing));
cwarn << " Block RLP:" << RLP(_block); badBlockInfo(bi, _err);
}
void badBlockHeader(bytesConstRef _header, string const& _err)
{
badBlockInfo(BlockInfo::fromHeader(_header, CheckNothing), _err);
cwarn << " Header:" << toHex(_header);
cwarn << " Header RLP:" << RLP(_header);;
} }
} }

6
libethcore/Common.h

@ -85,6 +85,10 @@ using BlockNumber = unsigned;
static const BlockNumber LatestBlock = (BlockNumber)-2; static const BlockNumber LatestBlock = (BlockNumber)-2;
static const BlockNumber PendingBlock = (BlockNumber)-1; static const BlockNumber PendingBlock = (BlockNumber)-1;
static const h256 LatestBlockHash = h256(2);
static const h256 EarliestBlockHash = h256(1);
static const h256 PendingBlockHash = h256(0);
enum class RelativeBlock: BlockNumber enum class RelativeBlock: BlockNumber
{ {
@ -156,8 +160,6 @@ struct TransactionSkeleton
u256 gasPrice = UndefinedU256; 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); void badBlock(bytesConstRef _header, std::string const& _err);
inline void badBlock(bytes const& _header, std::string const& _err) { badBlock(&_header, _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_platformId = 0;
unsigned Ethash::GPUMiner::s_deviceId = 0; unsigned Ethash::GPUMiner::s_deviceId = 0;
unsigned Ethash::GPUMiner::s_numInstances = 0; unsigned Ethash::GPUMiner::s_numInstances = 0;
unsigned Ethash::GPUMiner::s_dagChunks = 1;
Ethash::GPUMiner::GPUMiner(ConstructionInfo const& _ci): Ethash::GPUMiner::GPUMiner(ConstructionInfo const& _ci):
Miner(_ci), Miner(_ci),
@ -335,18 +334,19 @@ void Ethash::GPUMiner::workLoop()
EthashAux::FullType dag; EthashAux::FullType dag;
while (true) while (true)
{ {
if ((dag = EthashAux::full(w.seedHash, false))) if ((dag = EthashAux::full(w.seedHash, true)))
break; break;
if (shouldStop()) if (shouldStop())
{ {
delete m_miner; delete m_miner;
m_miner = nullptr;
return; return;
} }
cnote << "Awaiting DAG"; cnote << "Awaiting DAG";
this_thread::sleep_for(chrono::milliseconds(500)); this_thread::sleep_for(chrono::milliseconds(500));
} }
bytesConstRef dagData = dag->data(); bytesConstRef dagData = dag->data();
m_miner->init(dagData.data(), dagData.size(), 32, s_platformId, device, s_dagChunks); m_miner->init(dagData.data(), dagData.size(), 32, s_platformId, device);
} }
uint64_t upper64OfBoundary = (uint64_t)(u64)((u256)w.boundary >> 192); uint64_t upper64OfBoundary = (uint64_t)(u64)((u256)w.boundary >> 192);
@ -354,6 +354,8 @@ void Ethash::GPUMiner::workLoop()
} }
catch (cl::Error const& _e) catch (cl::Error const& _e)
{ {
delete m_miner;
m_miner = nullptr;
cwarn << "Error GPU mining: " << _e.what() << "(" << _e.err() << ")"; cwarn << "Error GPU mining: " << _e.what() << "(" << _e.err() << ")";
} }
} }
@ -364,11 +366,6 @@ void Ethash::GPUMiner::pause()
stopWorking(); stopWorking();
} }
bool Ethash::GPUMiner::haveSufficientGPUMemory()
{
return ethash_cl_miner::haveSufficientGPUMemory(s_platformId);
}
std::string Ethash::GPUMiner::platformInfo() std::string Ethash::GPUMiner::platformInfo()
{ {
return ethash_cl_miner::platform_info(s_platformId, s_deviceId); return ethash_cl_miner::platform_info(s_platformId, s_deviceId);
@ -376,7 +373,17 @@ std::string Ethash::GPUMiner::platformInfo()
unsigned Ethash::GPUMiner::getNumDevices() unsigned Ethash::GPUMiner::getNumDevices()
{ {
return ethash_cl_miner::get_num_devices(s_platformId); return ethash_cl_miner::getNumDevices(s_platformId);
}
void Ethash::GPUMiner::listDevices()
{
return ethash_cl_miner::listDevices();
}
bool Ethash::GPUMiner::configureGPU()
{
return ethash_cl_miner::configureGPU();
} }
#endif #endif

8
libethcore/Ethash.h

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

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_data = boost::error_info<struct tag_data, std::string>;
using errinfo_nonce = boost::error_info<struct tag_nonce, h64>; using errinfo_nonce = boost::error_info<struct tag_nonce, h64>;
using errinfo_difficulty = boost::error_info<struct tag_difficulty, u256>; 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>; using BadFieldError = boost::tuple<errinfo_field, errinfo_data>;
struct DatabaseAlreadyOpen: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(OutOfGasBase);
struct OutOfGasBase: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(OutOfGasIntrinsic);
struct NotEnoughAvailableSpace: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(NotEnoughAvailableSpace);
struct NotEnoughCash: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(NotEnoughCash);
struct GasPriceTooLow: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(GasPriceTooLow);
struct BlockGasLimitReached: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(BlockGasLimitReached);
struct NoSuchContract: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(FeeTooSmall);
struct ContractAddressCollision: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(TooMuchGasUsed);
struct FeeTooSmall: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(ExtraDataTooBig);
struct TooMuchGasUsed: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(InvalidSignature);
struct ExtraDataTooBig: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(InvalidBlockFormat);
struct InvalidSignature: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(InvalidUnclesHash);
struct InvalidBlockFormat: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(TooManyUncles);
struct InvalidUnclesHash: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(UncleTooOld);
struct InvalidUncle: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(UncleIsBrother);
struct TooManyUncles: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(UncleInChain);
struct UncleTooOld: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(InvalidStateRoot);
struct UncleIsBrother: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(InvalidGasUsed);
struct UncleInChain: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(InvalidTransactionsRoot);
struct DuplicateUncleNonce: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(InvalidDifficulty);
struct InvalidStateRoot: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(InvalidGasLimit);
struct InvalidGasUsed: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(InvalidReceiptsStateRoot);
struct InvalidTransactionsHash: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(InvalidTimestamp);
struct InvalidTransaction: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(InvalidLogBloom);
struct InvalidDifficulty: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(InvalidNonce);
struct InvalidGasLimit: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(InvalidBlockHeaderItemCount);
struct InvalidTransactionGasUsed: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(InvalidBlockNonce);
struct InvalidTransactionsStateRoot: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(InvalidParentHash);
struct InvalidReceiptsStateRoot: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(InvalidNumber);
struct InvalidTimestamp: virtual dev::Exception {};
struct InvalidLogBloom: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(DatabaseAlreadyOpen);
struct InvalidNonce: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(DAGCreationFailure);
struct InvalidBlockHeaderItemCount: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(DAGComputeFailure);
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 {};
} }
} }

267
libethereum/BlockChain.cpp

@ -35,6 +35,7 @@
#include <libdevcore/StructuredLogger.h> #include <libdevcore/StructuredLogger.h>
#include <libdevcore/FileSystem.h> #include <libdevcore/FileSystem.h>
#include <libethcore/Exceptions.h> #include <libethcore/Exceptions.h>
#include <libethcore/EthashAux.h>
#include <libethcore/ProofOfWork.h> #include <libethcore/ProofOfWork.h>
#include <libethcore/BlockInfo.h> #include <libethcore/BlockInfo.h>
#include <libethcore/Params.h> #include <libethcore/Params.h>
@ -42,13 +43,14 @@
#include "GenesisInfo.h" #include "GenesisInfo.h"
#include "State.h" #include "State.h"
#include "Defaults.h" #include "Defaults.h"
using namespace std; using namespace std;
using namespace dev; using namespace dev;
using namespace dev::eth; using namespace dev::eth;
namespace js = json_spirit; namespace js = json_spirit;
#define ETH_CATCH 1 #define ETH_CATCH 1
#define ETH_TIMED_IMPORTS 0 #define ETH_TIMED_IMPORTS 1
#ifdef _WIN32 #ifdef _WIN32
const char* BlockChainDebug::name() { return EthBlue "8" EthWhite " <>"; } 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); // _bq.tick(*this);
vector<pair<BlockInfo, bytes>> blocks; VerifiedBlocks blocks;
_bq.drain(blocks, _max); _bq.drain(blocks, _max);
h256s fresh; h256s fresh;
h256s dead; h256s dead;
h256s badBlocks; h256s badBlocks;
for (auto const& block: blocks) for (VerifiedBlock const& block: blocks)
{ if (!badBlocks.empty())
try badBlocks.push_back(block.verified.info.hash());
{ else
// 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)
{ {
cnote << "Exception while importing block. Someone (Jeff? That you?) seems to be giving us dodgy blocks!" << LogTag::Error << diagnostic_information(_e); try
// NOTE: don't reimport since the queue should guarantee everything in the right order. {
// Can't continue - chain bad. // Nonce & uncle nonces already verified in verification thread at this point.
badBlocks.push_back(block.first.hash()); 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)); return make_tuple(fresh, dead, _bq.doneDrain(badBlocks));
} }
@ -346,7 +360,7 @@ pair<ImportResult, ImportRoute> BlockChain::attemptImport(bytes const& _block, O
{ {
try try
{ {
return make_pair(ImportResult::Success, import(_block, _stateDB, _ir)); return make_pair(ImportResult::Success, import(verifyBlock(_block, m_onBad), _stateDB, _ir));
} }
catch (UnknownParent&) catch (UnknownParent&)
{ {
@ -360,8 +374,10 @@ pair<ImportResult, ImportRoute> BlockChain::attemptImport(bytes const& _block, O
{ {
return make_pair(ImportResult::FutureTime, make_pair(h256s(), h256s())); 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())); 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) ImportRoute BlockChain::import(bytes const& _block, OverlayDB const& _db, ImportRequirements::value _ir)
{ {
// VERIFY: populates from the block and checks the block is internally coherent. // VERIFY: populates from the block and checks the block is internally coherent.
BlockInfo bi; VerifiedBlockRef block;
#if ETH_CATCH #if ETH_CATCH
try try
#endif #endif
{ {
bi.populate(&_block); block = verifyBlock(_block, m_onBad);
bi.verifyInternals(&_block);
} }
#if ETH_CATCH #if ETH_CATCH
catch (Exception const& _e) catch (Exception& ex)
{ {
clog(BlockChainNote) << " Malformed block: " << diagnostic_information(_e); // clog(BlockChainNote) << " Malformed block: " << diagnostic_information(ex);
_e << errinfo_comment("Malformed block "); ex << errinfo_now(time(0));
ex << errinfo_block(_block);
throw; throw;
} }
#endif #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. //@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 #endif
// Check block doesn't already exist first! // 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()); BOOST_THROW_EXCEPTION(AlreadyHaveBlock());
} }
// Work out its number as the parent's number + 1 // 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. // 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()); BOOST_THROW_EXCEPTION(UnknownParent());
} }
auto pd = details(_bi.parentHash); auto pd = details(_block.info.parentHash);
if (!pd) if (!pd)
{ {
auto pdata = pd.rlp(); auto pdata = pd.rlp();
clog(BlockChainDebug) << "Details is returning false despite block known:" << RLP(pdata); clog(BlockChainDebug) << "Details is returning false despite block known:" << RLP(pdata);
auto parentBlock = block(_bi.parentHash); auto parentBlock = block(_block.info.parentHash);
clog(BlockChainDebug) << "isKnown:" << isKnown(_bi.parentHash); clog(BlockChainDebug) << "isKnown:" << isKnown(_block.info.parentHash);
clog(BlockChainDebug) << "last/number:" << m_lastBlockNumber << m_lastBlockHash << _bi.number; clog(BlockChainDebug) << "last/number:" << m_lastBlockNumber << m_lastBlockHash << _block.info.number;
clog(BlockChainDebug) << "Block:" << BlockInfo(parentBlock); clog(BlockChainDebug) << "Block:" << BlockInfo(parentBlock);
clog(BlockChainDebug) << "RLP:" << RLP(parentBlock); clog(BlockChainDebug) << "RLP:" << RLP(parentBlock);
clog(BlockChainDebug) << "DATABASE CORRUPTION: CRITICAL FAILURE"; 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 // 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. // Block has a timestamp in the future. This is no good.
BOOST_THROW_EXCEPTION(FutureTime()); BOOST_THROW_EXCEPTION(FutureTime());
} }
clog(BlockChainChat) << "Attempting import of " << _bi.hash() << "..."; clog(BlockChainChat) << "Attempting import of " << _block.info.hash() << "...";
#if ETH_TIMED_IMPORTS #if ETH_TIMED_IMPORTS
preliminaryChecks = t.elapsed(); 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. // 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. // Get total difficulty increase and update state, checking it.
State s(_db); State s(_db);
auto tdIncrease = s.enactOn(&_block, _bi, *this, _ir); auto tdIncrease = s.enactOn(_block, *this, _ir);
BlockLogBlooms blb; BlockLogBlooms blb;
BlockReceipts br; BlockReceipts br;
@ -470,14 +486,8 @@ ImportRoute BlockChain::import(BlockInfo const& _bi, bytes const& _block, Overla
blb.blooms.push_back(s.receipt(i).bloom()); blb.blooms.push_back(s.receipt(i).bloom());
br.receipts.push_back(s.receipt(i)); br.receipts.push_back(s.receipt(i));
} }
try {
s.cleanup(true); s.cleanup(true);
}
catch (BadRoot)
{
cwarn << "BadRoot error. Retrying import later.";
BOOST_THROW_EXCEPTION(FutureTime());
}
td = pd.totalDifficulty + tdIncrease; td = pd.totalDifficulty + tdIncrease;
@ -497,22 +507,22 @@ ImportRoute BlockChain::import(BlockInfo const& _bi, bytes const& _block, Overla
// together with an "ensureCachedWithUpdatableLock(l)" method. // together with an "ensureCachedWithUpdatableLock(l)" method.
// This is safe in practice since the caches don't get flushed nearly often enough to be // This is safe in practice since the caches don't get flushed nearly often enough to be
// done here. // done here.
details(_bi.parentHash); details(_block.info.parentHash);
DEV_WRITE_GUARDED(x_details) 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 #if ETH_TIMED_IMPORTS || !ETH_TRUE
collation = t.elapsed(); collation = t.elapsed();
t.restart(); t.restart();
#endif #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) 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(_block.info.hash(), ExtraDetails), (ldb::Slice)dev::ref(BlockDetails((unsigned)pd.number + 1, td, _block.info.parentHash, {}).rlp()));
extrasBatch.Put(toSlice(_bi.hash(), ExtraLogBlooms), (ldb::Slice)dev::ref(blb.rlp())); extrasBatch.Put(toSlice(_block.info.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(), ExtraReceipts), (ldb::Slice)dev::ref(br.rlp()));
#if ETH_TIMED_IMPORTS || !ETH_TRUE #if ETH_TIMED_IMPORTS || !ETH_TRUE
writing = t.elapsed(); writing = t.elapsed();
@ -520,30 +530,25 @@ ImportRoute BlockChain::import(BlockInfo const& _bi, bytes const& _block, Overla
#endif #endif
} }
#if ETH_CATCH #if ETH_CATCH
catch (InvalidNonce const& _e) catch (BadRoot& ex)
{ {
clog(BlockChainNote) << " Malformed block: " << diagnostic_information(_e); cwarn << "BadRoot error. Retrying import later.";
_e << errinfo_comment("Malformed block "); BOOST_THROW_EXCEPTION(FutureTime());
throw;
} }
catch (Exception const& _e) catch (Exception& ex)
{ {
clog(BlockChainWarn) << " Malformed block: " << diagnostic_information(_e); ex << errinfo_now(time(0));
_e << errinfo_comment("Malformed block "); ex << errinfo_block(_block.block.toBytes());
clog(BlockChainWarn) << "Block: " << _bi.hash();
clog(BlockChainWarn) << _bi;
clog(BlockChainWarn) << "Block parent: " << _bi.parentHash;
clog(BlockChainWarn) << BlockInfo(block(_bi.parentHash));
throw; throw;
} }
#endif #endif
StructuredLogger::chainReceivedNewBlock( StructuredLogger::chainReceivedNewBlock(
_bi.headerHash(WithoutNonce).abridged(), _block.info.headerHash(WithoutNonce).abridged(),
_bi.nonce.abridged(), _block.info.nonce.abridged(),
currentHash().abridged(), currentHash().abridged(),
"", // TODO: remote id ?? "", // TODO: remote id ??
_bi.parentHash.abridged() _block.info.parentHash.abridged()
); );
// cnote << "Parent " << bi.parentHash << " has " << details(bi.parentHash).children.size() << " children."; // 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... // don't include bi.hash() in treeRoute, since it's not yet in details DB...
// just tack it on afterwards. // just tack it on afterwards.
unsigned commonIndex; unsigned commonIndex;
tie(route, common, commonIndex) = treeRoute(last, _bi.parentHash); tie(route, common, commonIndex) = treeRoute(last, _block.info.parentHash);
route.push_back(_bi.hash()); 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 // Most of the time these two will be equal - only when we're doing a chain revert will they not be
if (common != last) 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) for (auto i = route.rbegin(); i != route.rend() && *i != common; ++i)
{ {
BlockInfo tbi; BlockInfo tbi;
if (*i == _bi.hash()) if (*i == _block.info.hash())
tbi = _bi; tbi = _block.info;
else else
tbi = BlockInfo(block(*i)); tbi = BlockInfo(block(*i));
@ -597,7 +602,7 @@ ImportRoute BlockChain::import(BlockInfo const& _bi, bytes const& _block, Overla
h256s newTransactionAddresses; h256s newTransactionAddresses;
{ {
bytes blockBytes; bytes blockBytes;
RLP blockRLP(*i == _bi.hash() ? _block : (blockBytes = block(*i))); RLP blockRLP(*i == _block.info.hash() ? _block.block : &(blockBytes = block(*i)));
TransactionAddress ta; TransactionAddress ta;
ta.blockHash = tbi.hash(); ta.blockHash = tbi.hash();
for (ta.index = 0; ta.index < blockRLP[1].itemCount(); ++ta.index) 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. // FINALLY! change our best hash.
{ {
newLastBlockHash = _bi.hash(); newLastBlockHash = _block.info.hash();
newLastBlockNumber = (unsigned)_bi.number; 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( StructuredLogger::chainNewHead(
_bi.headerHash(WithoutNonce).abridged(), _block.info.headerHash(WithoutNonce).abridged(),
_bi.nonce.abridged(), _block.info.nonce.abridged(),
currentHash().abridged(), currentHash().abridged(),
_bi.parentHash.abridged() _block.info.parentHash.abridged()
); );
} }
else else
@ -634,24 +639,26 @@ ImportRoute BlockChain::import(BlockInfo const& _bi, bytes const& _block, Overla
m_blocksDB->Write(m_writeOptions, &blocksBatch); m_blocksDB->Write(m_writeOptions, &blocksBatch);
m_extrasDB->Write(m_writeOptions, &extrasBatch); 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) << "Known block just inserted has no details.";
clog(BlockChainDebug) << "Block:" << _bi; clog(BlockChainDebug) << "Block:" << _block.info;
clog(BlockChainDebug) << "DATABASE CORRUPTION: CRITICAL FAILURE"; clog(BlockChainDebug) << "DATABASE CORRUPTION: CRITICAL FAILURE";
exit(-1); exit(-1);
} }
try { try {
State canary(_db, *this, _bi.hash(), ImportRequirements::DontHave); State canary(_db, *this, _block.info.hash(), ImportRequirements::DontHave);
} }
catch (...) catch (...)
{ {
clog(BlockChainDebug) << "Failed to initialise State object form imported block."; 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"; clog(BlockChainDebug) << "DATABASE CORRUPTION: CRITICAL FAILURE";
exit(-1); exit(-1);
} }
#endif
if (m_lastBlockHash != newLastBlockHash) if (m_lastBlockHash != newLastBlockHash)
DEV_WRITE_GUARDED(x_lastBlockHash) DEV_WRITE_GUARDED(x_lastBlockHash)
@ -667,12 +674,16 @@ ImportRoute BlockChain::import(BlockInfo const& _bi, bytes const& _block, Overla
#if ETH_TIMED_IMPORTS #if ETH_TIMED_IMPORTS
checkBest = t.elapsed(); checkBest = t.elapsed();
cnote << "Import took:" << total.elapsed(); if (total.elapsed() > 1.0)
cnote << "preliminaryChecks:" << preliminaryChecks; {
cnote << "enactment:" << enactment; cnote << "SLOW IMPORT:" << _block.info.hash();
cnote << "collation:" << collation; cnote << " Import took:" << total.elapsed();
cnote << "writing:" << writing; cnote << " preliminaryChecks:" << preliminaryChecks;
cnote << "checkBest:" << checkBest; cnote << " enactment:" << enactment;
cnote << " collation:" << collation;
cnote << " writing:" << writing;
cnote << " checkBest:" << checkBest;
}
#endif #endif
if (!route.empty()) if (!route.empty())
@ -915,8 +926,8 @@ void BlockChain::checkConsistency()
delete it; delete it;
} }
static inline unsigned upow(unsigned a, unsigned b) { while (b-- > 0) a *= a; return a; } static inline unsigned upow(unsigned a, unsigned b) { if (!b) return 1; while (--b > 0) a *= a; return a; }
static inline unsigned ceilDiv(unsigned n, unsigned d) { return n / (n + d - 1); } static inline unsigned ceilDiv(unsigned n, unsigned d) { return (n + d - 1) / d; }
//static inline unsigned floorDivPow(unsigned n, unsigned a, unsigned b) { return n / upow(a, b); } //static inline unsigned floorDivPow(unsigned n, unsigned a, unsigned b) { return n / upow(a, b); }
//static inline unsigned ceilDivPow(unsigned n, unsigned a, unsigned b) { return ceilDiv(n, upow(a, b)); } //static inline unsigned ceilDivPow(unsigned n, unsigned a, unsigned b) { return ceilDiv(n, upow(a, b)); }
@ -1054,3 +1065,59 @@ bytes BlockChain::block(h256 const& _hash) const
return m_blocks[_hash]; 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 "Account.h"
#include "Transaction.h" #include "Transaction.h"
#include "BlockQueue.h" #include "BlockQueue.h"
#include "VerifiedBlock.h"
namespace ldb = leveldb; namespace ldb = leveldb;
namespace std namespace std
@ -120,7 +121,7 @@ public:
/// Import block into disk-backed DB /// Import block into disk-backed DB
/// @returns the block hashes of any blocks that came into/went out of the canonical block chain. /// @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(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). /// Returns true if the given block is known (though not necessarily a part of the canon chain).
bool isKnown(h256 const& _hash) const; bool isKnown(h256 const& _hash) const;
@ -143,6 +144,7 @@ public:
BlockLogBlooms logBlooms() const { return logBlooms(currentHash()); } BlockLogBlooms logBlooms() const { return logBlooms(currentHash()); }
/// Get the transactions' receipts of a block (or the most recent mined if none given). Thread-safe. /// Get the transactions' receipts of a block (or the most recent mined if none given). Thread-safe.
/// receipts are given in the same order are in the same order as the transactions
BlockReceipts receipts(h256 const& _hash) const { return queryExtras<BlockReceipts, ExtraReceipts>(_hash, m_receipts, x_receipts, NullBlockReceipts); } BlockReceipts receipts(h256 const& _hash) const { return queryExtras<BlockReceipts, ExtraReceipts>(_hash, m_receipts, x_receipts, NullBlockReceipts); }
BlockReceipts receipts() const { return receipts(currentHash()); } BlockReceipts receipts() const { return receipts(currentHash()); }
@ -256,6 +258,12 @@ public:
/// Deallocate unused data. /// Deallocate unused data.
void garbageCollect(bool _force = false); 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: private:
static h256 chunkId(unsigned _level, unsigned _index) { return h256(_index * 0xff + _level); } static h256 chunkId(unsigned _level, unsigned _index) { return h256(_index * 0xff + _level); }
@ -335,6 +343,8 @@ private:
ldb::ReadOptions m_readOptions; ldb::ReadOptions m_readOptions;
ldb::WriteOptions m_writeOptions; 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); friend std::ostream& operator<<(std::ostream& _out, BlockChain const& _bc);
}; };

274
libethereum/BlockQueue.cpp

@ -22,10 +22,11 @@
#include "BlockQueue.h" #include "BlockQueue.h"
#include <thread> #include <thread>
#include <libdevcore/Log.h> #include <libdevcore/Log.h>
#include <libethcore/EthashAux.h>
#include <libethcore/Exceptions.h> #include <libethcore/Exceptions.h>
#include <libethcore/BlockInfo.h> #include <libethcore/BlockInfo.h>
#include "BlockChain.h" #include "BlockChain.h"
#include "VerifiedBlock.h"
#include "State.h"
using namespace std; using namespace std;
using namespace dev; using namespace dev;
using namespace dev::eth; using namespace dev::eth;
@ -36,8 +37,16 @@ const char* BlockQueueChannel::name() { return EthOrange "[]>"; }
const char* BlockQueueChannel::name() { return EthOrange "▣┅▶"; } const char* BlockQueueChannel::name() { return EthOrange "▣┅▶"; }
#endif #endif
size_t const c_maxKnownCount = 100000;
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 // Allow some room for other activity
unsigned verifierThreads = std::max(thread::hardware_concurrency(), 3U) - 2U; unsigned verifierThreads = std::max(thread::hardware_concurrency(), 3U) - 2U;
@ -56,11 +65,29 @@ BlockQueue::~BlockQueue()
i.join(); i.join();
} }
void BlockQueue::clear()
{
WriteGuard l(m_lock);
DEV_INVARIANT_CHECK;
Guard l2(m_verification);
m_readySet.clear();
m_drainingSet.clear();
m_verified.clear();
m_unverified.clear();
m_unknownSet.clear();
m_unknown.clear();
m_future.clear();
m_unknownSize = 0;
m_unknownCount = 0;
m_knownSize = 0;
m_knownCount = 0;
}
void BlockQueue::verifierBody() void BlockQueue::verifierBody()
{ {
while (!m_deleting) while (!m_deleting)
{ {
std::pair<h256, bytes> work; UnverifiedBlock work;
{ {
unique_lock<Mutex> l(m_verification); unique_lock<Mutex> l(m_verification);
@ -70,63 +97,16 @@ void BlockQueue::verifierBody()
swap(work, m_unverified.front()); swap(work, m_unverified.front());
m_unverified.pop_front(); m_unverified.pop_front();
BlockInfo bi; BlockInfo bi;
bi.mixHash = work.first; bi.mixHash = work.hash;
m_verifying.push_back(make_pair(bi, bytes())); bi.parentHash = work.parentHash;
m_verifying.push_back(VerifiedBlock { VerifiedBlockRef { bytesConstRef(), move(bi), Transactions() }, bytes() });
} }
std::pair<BlockInfo, bytes> res; VerifiedBlock res;
swap(work.second, res.second); swap(work.block, res.blockData);
try { try
try { {
res.first.populate(res.second, CheckEverything, work.first); res.verified = BlockChain::verifyBlock(res.blockData, m_onBad);
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;
}
}
} }
catch (...) catch (...)
{ {
@ -135,33 +115,46 @@ void BlockQueue::verifierBody()
// has to be this order as that's how invariants() assumes. // has to be this order as that's how invariants() assumes.
WriteGuard l2(m_lock); WriteGuard l2(m_lock);
unique_lock<Mutex> l(m_verification); unique_lock<Mutex> l(m_verification);
m_readySet.erase(work.first); m_readySet.erase(work.hash);
m_knownBad.insert(work.first); m_knownBad.insert(work.hash);
} }
unique_lock<Mutex> l(m_verification); unique_lock<Mutex> l(m_verification);
for (auto it = m_verifying.begin(); it != m_verifying.end(); ++it) for (auto it = m_verifying.begin(); it != m_verifying.end(); ++it)
if (it->first.mixHash == work.first) if (it->verified.info.mixHash == work.hash)
{ {
m_verifying.erase(it); m_verifying.erase(it);
goto OK1; 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:; OK1:;
continue; continue;
} }
bool ready = false; bool ready = false;
{ {
WriteGuard l2(m_lock);
unique_lock<Mutex> l(m_verification); unique_lock<Mutex> l(m_verification);
if (m_verifying.front().first.mixHash == work.first) if (!m_verifying.empty() && m_verifying.front().verified.info.mixHash == work.hash)
{ {
// we're next! // we're next!
m_verifying.pop_front(); m_verifying.pop_front();
m_verified.push_back(move(res)); if (m_knownBad.count(res.verified.info.parentHash))
while (m_verifying.size() && !m_verifying.front().second.empty()) {
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(); m_verifying.pop_front();
} }
ready = true; ready = true;
@ -169,12 +162,12 @@ void BlockQueue::verifierBody()
else else
{ {
for (auto& i: m_verifying) for (auto& i: m_verifying)
if (i.first.mixHash == work.first) if (i.verified.info.mixHash == work.hash)
{ {
i = move(res); i = move(res);
goto OK; 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:; OK:;
} }
} }
@ -234,6 +227,8 @@ ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc, boo
if (strftime(buf, 24, "%X", localtime(&bit)) == 0) if (strftime(buf, 24, "%X", localtime(&bit)) == 0)
buf[0] = '\0'; // empty if case strftime fails buf[0] = '\0'; // empty if case strftime fails
cblockq << "OK - queued for future [" << bi.timestamp << "vs" << time(0) << "] - will wait until" << buf; cblockq << "OK - queued for future [" << bi.timestamp << "vs" << time(0) << "] - will wait until" << buf;
m_unknownSize += _block.size();
m_unknownCount++;
return ImportResult::FutureTime; return ImportResult::FutureTime;
} }
else else
@ -242,6 +237,7 @@ ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc, boo
if (m_knownBad.count(bi.parentHash)) if (m_knownBad.count(bi.parentHash))
{ {
m_knownBad.insert(bi.hash()); m_knownBad.insert(bi.hash());
updateBad(bi.hash());
// bad parent; this is bad too, note it as such // bad parent; this is bad too, note it as such
return ImportResult::BadChain; return ImportResult::BadChain;
} }
@ -251,6 +247,8 @@ ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc, boo
cblockq << "OK - queued as unknown parent:" << bi.parentHash; cblockq << "OK - queued as unknown parent:" << bi.parentHash;
m_unknown.insert(make_pair(bi.parentHash, make_pair(h, _block.toBytes()))); m_unknown.insert(make_pair(bi.parentHash, make_pair(h, _block.toBytes())));
m_unknownSet.insert(h); m_unknownSet.insert(h);
m_unknownSize += _block.size();
m_unknownCount++;
return ImportResult::UnknownParent; return ImportResult::UnknownParent;
} }
@ -259,9 +257,11 @@ ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc, boo
// If valid, append to blocks. // If valid, append to blocks.
cblockq << "OK - ready for chain insertion."; cblockq << "OK - ready for chain insertion.";
DEV_GUARDED(m_verification) DEV_GUARDED(m_verification)
m_unverified.push_back(make_pair(h, _block.toBytes())); m_unverified.push_back(UnverifiedBlock { h, bi.parentHash, _block.toBytes() });
m_moreToVerify.notify_one(); m_moreToVerify.notify_one();
m_readySet.insert(h); m_readySet.insert(h);
m_knownSize += _block.size();
m_knownCount++;
noteReady_WITH_LOCK(h); noteReady_WITH_LOCK(h);
@ -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; DEV_INVARIANT_CHECK;
m_drainingSet.clear(); DEV_GUARDED(m_verification)
if (_bad.size())
{ {
vector<pair<BlockInfo, bytes>> old; collectUnknownBad(_bad);
DEV_GUARDED(m_verification) bool moreBad = true;
swap(m_verified, old); while (moreBad)
for (auto& b: old)
{ {
if (m_knownBad.count(b.first.parentHash)) moreBad = false;
{ std::vector<VerifiedBlock> oldVerified;
m_knownBad.insert(b.first.hash()); swap(m_verified, oldVerified);
m_readySet.erase(b.first.hash()); for (auto& b: oldVerified)
} if (m_knownBad.count(b.verified.info.parentHash) || m_knownBad.count(b.verified.info.hash()))
else {
DEV_GUARDED(m_verification) 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)); 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) void BlockQueue::tick(BlockChain const& _bc)
@ -317,7 +380,11 @@ void BlockQueue::tick(BlockChain const& _bc)
DEV_INVARIANT_CHECK; DEV_INVARIANT_CHECK;
auto end = m_future.lower_bound(t); auto end = m_future.lower_bound(t);
for (auto i = m_future.begin(); i != end; ++i) for (auto i = m_future.begin(); i != end; ++i)
{
m_unknownSize -= i->second.second.size();
m_unknownCount--;
todo.push_back(move(i->second)); todo.push_back(move(i->second));
}
m_future.erase(m_future.begin(), end); m_future.erase(m_future.begin(), end);
} }
} }
@ -348,12 +415,24 @@ QueueStatus BlockQueue::blockStatus(h256 const& _h) const
QueueStatus::Unknown; 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); WriteGuard l(m_lock);
DEV_INVARIANT_CHECK; DEV_INVARIANT_CHECK;
if (m_drainingSet.empty()) if (m_drainingSet.empty())
{ {
bool wasFull = knownFull();
DEV_GUARDED(m_verification) DEV_GUARDED(m_verification)
{ {
o_out.resize(min<unsigned>(_max, m_verified.size())); o_out.resize(min<unsigned>(_max, m_verified.size()));
@ -364,11 +443,16 @@ void BlockQueue::drain(std::vector<std::pair<BlockInfo, bytes>>& o_out, unsigned
for (auto const& bs: o_out) for (auto const& bs: o_out)
{ {
// TODO: @optimise use map<h256, bytes> rather than vector<bytes> & set<h256>. // 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_drainingSet.insert(h);
m_readySet.erase(h); m_readySet.erase(h);
m_knownSize -= bs.verified.block.size();
m_knownCount--;
} }
if (wasFull && !knownFull())
m_onRoomAvailable();
} }
} }
bool BlockQueue::invariants() const bool BlockQueue::invariants() const
@ -389,7 +473,11 @@ void BlockQueue::noteReady_WITH_LOCK(h256 const& _good)
for (auto it = r.first; it != r.second; ++it) for (auto it = r.first; it != r.second; ++it)
{ {
DEV_GUARDED(m_verification) DEV_GUARDED(m_verification)
m_unverified.push_back(it->second); m_unverified.push_back(UnverifiedBlock { it->second.first, it->first, it->second.second });
m_knownSize += it->second.second.size();
m_knownCount++;
m_unknownSize -= it->second.second.size();
m_unknownCount--;
auto newReady = it->second.first; auto newReady = it->second.first;
m_unknownSet.erase(newReady); m_unknownSet.erase(newReady);
m_readySet.insert(newReady); m_readySet.insert(newReady);
@ -400,6 +488,7 @@ void BlockQueue::noteReady_WITH_LOCK(h256 const& _good)
} }
if (notify) if (notify)
m_moreToVerify.notify_all(); m_moreToVerify.notify_all();
DEV_INVARIANT_CHECK;
} }
void BlockQueue::retryAllUnknown() void BlockQueue::retryAllUnknown()
@ -409,18 +498,23 @@ void BlockQueue::retryAllUnknown()
for (auto it = m_unknown.begin(); it != m_unknown.end(); ++it) for (auto it = m_unknown.begin(); it != m_unknown.end(); ++it)
{ {
DEV_GUARDED(m_verification) DEV_GUARDED(m_verification)
m_unverified.push_back(it->second); m_unverified.push_back(UnverifiedBlock { it->second.first, it->first, it->second.second });
auto newReady = it->second.first; auto newReady = it->second.first;
m_unknownSet.erase(newReady); m_unknownSet.erase(newReady);
m_readySet.insert(newReady); m_readySet.insert(newReady);
m_knownCount++;
m_moreToVerify.notify_one(); m_moreToVerify.notify_one();
} }
m_unknown.clear(); m_unknown.clear();
m_knownSize += m_unknownSize;
m_unknownSize = 0;
m_unknownCount = 0;
m_moreToVerify.notify_all(); m_moreToVerify.notify_all();
} }
std::ostream& dev::eth::operator<<(std::ostream& _out, BlockQueueStatus const& _bqs) std::ostream& dev::eth::operator<<(std::ostream& _out, BlockQueueStatus const& _bqs)
{ {
_out << "importing: " << _bqs.importing << endl;
_out << "verified: " << _bqs.verified << endl; _out << "verified: " << _bqs.verified << endl;
_out << "verifying: " << _bqs.verifying << endl; _out << "verifying: " << _bqs.verifying << endl;
_out << "unverified: " << _bqs.unverified << endl; _out << "unverified: " << _bqs.unverified << endl;

38
libethereum/BlockQueue.h

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

11
libethereum/CMakeLists.txt

@ -14,6 +14,10 @@ aux_source_directory(. SRC_LIST)
include_directories(BEFORE ..) include_directories(BEFORE ..)
include_directories(${LEVELDB_INCLUDE_DIRS}) include_directories(${LEVELDB_INCLUDE_DIRS})
include_directories(${Boost_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) set(EXECUTABLE ethereum)
@ -30,6 +34,13 @@ target_link_libraries(${EXECUTABLE} ethcore)
target_link_libraries(${EXECUTABLE} ${LEVELDB_LIBRARIES}) target_link_libraries(${EXECUTABLE} ${LEVELDB_LIBRARIES})
target_link_libraries(${EXECUTABLE} ${Boost_REGEX_LIBRARIES}) target_link_libraries(${EXECUTABLE} ${Boost_REGEX_LIBRARIES})
target_link_libraries(${EXECUTABLE} secp256k1) 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) if (CMAKE_COMPILER_IS_MINGW)
target_link_libraries(${EXECUTABLE} ssp shlwapi) target_link_libraries(${EXECUTABLE} ssp shlwapi)

280
libethereum/Client.cpp

@ -25,9 +25,16 @@
#include <thread> #include <thread>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <boost/math/distributions/normal.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/Log.h>
#include <libdevcore/StructuredLogger.h> #include <libdevcore/StructuredLogger.h>
#include <libp2p/Host.h> #include <libp2p/Host.h>
#if ETH_JSONRPC || !ETH_TRUE
#include "Sentinel.h"
#endif
#include "Defaults.h" #include "Defaults.h"
#include "Executive.h" #include "Executive.h"
#include "EthereumHost.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) void BasicGasPricer::update(BlockChain const& _bc)
{ {
unsigned c = 0; 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): 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_vc(_dbPath),
m_bc(_dbPath, max(m_vc.action(), _forceAction), [](unsigned d, unsigned t){ cerr << "REVISING BLOCKCHAIN: Processed " << d << " of " << t << "...\r"; }), m_bc(_dbPath, max(m_vc.action(), _forceAction), [](unsigned d, unsigned t){ cerr << "REVISING BLOCKCHAIN: Processed " << d << " of " << t << "...\r"; }),
m_gp(_gp), 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_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_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_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_farm.onSolutionFound([=](ProofOfWork::Solution const& s){ return this->submitWork(s); });
m_gp->update(m_bc); m_gp->update(m_bc);
@ -206,6 +323,18 @@ Client::~Client()
stopWorking(); 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) void Client::setNetworkId(u256 _n)
{ {
if (auto h = m_host.lock()) if (auto h = m_host.lock())
@ -299,21 +428,17 @@ void Client::killChain()
void Client::clearPending() void Client::clearPending()
{ {
h256Hash changeds;
DEV_WRITE_GUARDED(x_postMine) DEV_WRITE_GUARDED(x_postMine)
{ {
if (!m_postMine.pending().size()) if (!m_postMine.pending().size())
return; return;
// for (unsigned i = 0; i < m_postMine.pending().size(); ++i)
// appendFromNewPending(m_postMine.logBloom(i), changeds);
changeds.insert(PendingChangedFilter);
m_tq.clear(); m_tq.clear();
DEV_READ_GUARDED(x_preMine) DEV_READ_GUARDED(x_preMine)
m_postMine = m_preMine; m_postMine = m_preMine;
} }
startMining(); startMining();
h256Hash changeds;
noteChanged(changeds); noteChanged(changeds);
} }
@ -336,47 +461,53 @@ static S& filtersStreamOut(S& _out, T const& _fs)
return _out; return _out;
} }
void Client::appendFromNewPending(TransactionReceipt const& _receipt, h256Hash& io_changed, h256 _transactionHash) void Client::appendFromNewPending(TransactionReceipt const& _receipt, h256Hash& io_changed, h256 _sha3)
{ {
Guard l(x_filtersWatches); Guard l(x_filtersWatches);
io_changed.insert(PendingChangedFilter);
m_specialFilters.at(PendingChangedFilter).push_back(_sha3);
for (pair<h256 const, InstalledFilter>& i: m_filters) for (pair<h256 const, InstalledFilter>& i: m_filters)
if (i.second.filter.envelops(RelativeBlock::Pending, m_bc.number() + 1)) {
// acceptable number.
auto m = i.second.filter.matches(_receipt);
if (m.size())
{ {
// acceptable number. // filter catches them
auto m = i.second.filter.matches(_receipt); for (LogEntry const& l: m)
if (m.size()) i.second.changes.push_back(LocalisedLogEntry(l));
{ io_changed.insert(i.first);
// filter catches them
for (LogEntry const& l: m)
i.second.changes.push_back(LocalisedLogEntry(l, m_bc.number() + 1, _transactionHash));
io_changed.insert(i.first);
}
} }
}
} }
void Client::appendFromNewBlock(h256 const& _block, h256Hash& io_changed) void Client::appendFromNewBlock(h256 const& _block, h256Hash& io_changed)
{ {
// TODO: more precise check on whether the txs match. // TODO: more precise check on whether the txs match.
auto d = m_bc.info(_block); auto d = m_bc.info(_block);
auto br = m_bc.receipts(_block); auto receipts = m_bc.receipts(_block).receipts;
Guard l(x_filtersWatches); Guard l(x_filtersWatches);
io_changed.insert(ChainChangedFilter);
m_specialFilters.at(ChainChangedFilter).push_back(_block);
for (pair<h256 const, InstalledFilter>& i: m_filters) for (pair<h256 const, InstalledFilter>& i: m_filters)
if (i.second.filter.envelops(RelativeBlock::Latest, d.number) && i.second.filter.matches(d.logBloom)) {
// acceptable number & looks like block may contain a matching log entry. // acceptable number & looks like block may contain a matching log entry.
for (size_t j = 0; j < br.receipts.size(); j++) unsigned logIndex = 0;
for (size_t j = 0; j < receipts.size(); j++)
{
logIndex++;
auto tr = receipts[j];
auto m = i.second.filter.matches(tr);
if (m.size())
{ {
auto tr = br.receipts[j]; auto transactionHash = transaction(d.hash(), j).sha3();
auto m = i.second.filter.matches(tr); // filter catches them
if (m.size()) for (LogEntry const& l: m)
{ i.second.changes.push_back(LocalisedLogEntry(l, d, transactionHash, j, logIndex));
auto transactionHash = transaction(d.hash(), j).sha3(); io_changed.insert(i.first);
// filter catches them
for (LogEntry const& l: m)
i.second.changes.push_back(LocalisedLogEntry(l, (unsigned)d.number, transactionHash));
io_changed.insert(i.first);
}
} }
}
}
} }
void Client::setForceMining(bool _enable) void Client::setForceMining(bool _enable)
@ -448,6 +579,9 @@ ProofOfWork::WorkPackage Client::getWork()
bool oldShould = shouldServeWork(); bool oldShould = shouldServeWork();
m_lastGetWork = chrono::system_clock::now(); 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 this request has made us bother to serve work, prep it now.
if (!oldShould && shouldServeWork()) if (!oldShould && shouldServeWork())
onPostStateChanged(); onPostStateChanged();
@ -480,13 +614,10 @@ bool Client::submitWork(ProofOfWork::Solution const& _solution)
void Client::syncBlockQueue() void Client::syncBlockQueue()
{ {
ImportRoute ir; ImportRoute ir;
cwork << "BQ ==> CHAIN ==> STATE"; cwork << "BQ ==> CHAIN ==> STATE";
{ tie(ir.first, ir.second, m_syncBlockQueue) = m_bc.sync(m_bq, m_stateDB, rand() % 10 + 5);
tie(ir.first, ir.second, m_syncBlockQueue) = m_bc.sync(m_bq, m_stateDB, rand() % 90 + 10); if (ir.first.empty())
if (ir.first.empty()) return;
return;
}
onChainChanged(ir); onChainChanged(ir);
} }
@ -511,7 +642,7 @@ void Client::syncTransactionQueue()
DEV_READ_GUARDED(x_postMine) DEV_READ_GUARDED(x_postMine)
for (size_t i = 0; i < newPendingReceipts.size(); i++) for (size_t i = 0; i < newPendingReceipts.size(); i++)
appendFromNewPending(newPendingReceipts[i], changeds, m_postMine.pending()[i].sha3()); appendFromNewPending(newPendingReceipts[i], changeds, m_postMine.pending()[i].sha3());
changeds.insert(PendingChangedFilter);
// Tell farm about new transaction (i.e. restartProofOfWork mining). // Tell farm about new transaction (i.e. restartProofOfWork mining).
onPostStateChanged(); onPostStateChanged();
@ -554,7 +685,6 @@ void Client::onChainChanged(ImportRoute const& _ir)
h256Hash changeds; h256Hash changeds;
for (auto const& h: _ir.first) for (auto const& h: _ir.first)
appendFromNewBlock(h, changeds); appendFromNewBlock(h, changeds);
changeds.insert(ChainChangedFilter);
// RESTART MINING // RESTART MINING
@ -605,11 +735,22 @@ bool Client::remoteActive() const
void Client::onPostStateChanged() 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) DEV_WRITE_GUARDED(x_working)
m_working.commitToMine(m_bc); m_working.commitToMine(m_bc);
DEV_READ_GUARDED(x_working) DEV_READ_GUARDED(x_working)
@ -618,20 +759,21 @@ void Client::onPostStateChanged()
m_postMine = m_working; m_postMine = m_working;
m_miningInfo = m_postMine.info(); m_miningInfo = m_postMine.info();
} }
m_farm.setWork(m_miningInfo);
Ethash::ensurePrecomputed(m_bc.number()); if (m_wouldMine)
} {
m_remoteWorking = false; m_farm.setWork(m_miningInfo);
} if (m_turboMining)
m_farm.startGPU();
else
m_farm.startCPU();
void Client::startMining() m_farm.setWork(m_miningInfo);
{ Ethash::ensurePrecomputed(m_bc.number());
if (m_turboMining) }
m_farm.startGPU(); }
else if (!m_wouldMine)
m_farm.startCPU(); m_farm.stop();
onPostStateChanged();
} }
void Client::noteChanged(h256Hash const& _filters) void Client::noteChanged(h256Hash const& _filters)
@ -648,15 +790,18 @@ void Client::noteChanged(h256Hash const& _filters)
cwatch << "!!!" << w.first << w.second.id.abridged(); cwatch << "!!!" << w.first << w.second.id.abridged();
w.second.changes += m_filters.at(w.second.id).changes; w.second.changes += m_filters.at(w.second.id).changes;
} }
else else if (m_specialFilters.count(w.second.id))
{ for (h256 const& hash: m_specialFilters.at(w.second.id))
cwatch << "!!!" << w.first << LogTag::Special << (w.second.id == PendingChangedFilter ? "pending" : w.second.id == ChainChangedFilter ? "chain" : "???"); {
w.second.changes.push_back(LocalisedLogEntry(SpecialLogEntry, 0)); cwatch << "!!!" << w.first << LogTag::Special << (w.second.id == PendingChangedFilter ? "pending" : w.second.id == ChainChangedFilter ? "chain" : "???");
} w.second.changes.push_back(LocalisedLogEntry(SpecialLogEntry, hash));
}
} }
// clear the filters now. // clear the filters now.
for (auto& i: m_filters) for (auto& i: m_filters)
i.second.changes.clear(); i.second.changes.clear();
for (auto& i: m_specialFilters)
i.second.clear();
} }
void Client::doWork() void Client::doWork()
@ -716,7 +861,16 @@ void Client::checkWatchGarbage()
State Client::asOf(h256 const& _block) const 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() void Client::prepareForTransaction()
@ -746,3 +900,9 @@ void Client::flushTransactions()
{ {
doWork(); 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()); 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. /// 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] // [PRIVATE API - only relevant for base clients, not available in general]
dev::eth::State state(unsigned _txi, h256 _block) const; dev::eth::State state(unsigned _txi, h256 _block) const;
@ -156,12 +156,14 @@ public:
CanonBlockChain const& blockChain() const { return m_bc; } CanonBlockChain const& blockChain() const { return m_bc; }
/// Get some information on the block queue. /// Get some information on the block queue.
BlockQueueStatus blockQueueStatus() const { return m_bq.status(); } BlockQueueStatus blockQueueStatus() const { return m_bq.status(); }
/// Get some information on the block queue.
HashChainStatus hashChainStatus() const;
/// Get the block queue. /// Get the block queue.
BlockQueue const& blockQueue() const { return m_bq; } BlockQueue const& blockQueue() const { return m_bq; }
// Mining stuff: // 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. /// Check block validity prior to mining.
bool miningParanoia() const { return m_paranoia; } bool miningParanoia() const { return m_paranoia; }
@ -176,14 +178,26 @@ public:
/// Enable/disable GPU mining. /// Enable/disable GPU mining.
void setTurboMining(bool _enable = true) { m_turboMining = _enable; if (isMining()) startMining(); } 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. /// Start mining.
/// NOT thread-safe - call it & stopMining only from a single thread /// NOT thread-safe - call it & stopMining only from a single thread
void startMining() override; void startMining() override;
/// Stop mining. /// Stop mining.
/// NOT thread-safe /// NOT thread-safe
void stopMining() override { m_farm.stop(); } void stopMining() override { m_wouldMine = false; rejigMining(); }
/// Are we mining now? /// Are we mining now?
bool isMining() const override { return m_farm.isMining(); } bool isMining() const override { return m_farm.isMining(); }
/// Are we mining now?
bool wouldMine() const override { return m_wouldMine; }
/// The hashrate... /// The hashrate...
uint64_t hashrate() const override; uint64_t hashrate() const override;
/// Check the progress of the mining. /// Check the progress of the mining.
@ -215,6 +229,8 @@ public:
void retryUnkonwn() { m_bq.retryAllUnknown(); } void retryUnkonwn() { m_bq.retryAllUnknown(); }
/// Get a report of activity. /// Get a report of activity.
ActivityReport activityReport() { ActivityReport ret; std::swap(m_report, ret); return ret; } 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: protected:
/// InterfaceStub methods /// InterfaceStub methods
@ -251,6 +267,9 @@ private:
/// Called when Worker is exiting. /// Called when Worker is exiting.
void doneWorking() override; 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. /// Magically called when the chain has changed. An import route is provided.
/// Called by either submitWork() or in our main thread through syncBlockQueue(). /// Called by either submitWork() or in our main thread through syncBlockQueue().
void onChainChanged(ImportRoute const& _ir); void onChainChanged(ImportRoute const& _ir);
@ -280,6 +299,10 @@ private:
/// @returns true only if it's worth bothering to prep the mining block. /// @returns true only if it's worth bothering to prep the mining block.
bool shouldServeWork() const { return m_bq.items().first == 0 && (isMining() || remoteActive()); } 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. VersionChecker m_vc; ///< Dummy object to check & update the protocol version.
CanonBlockChain m_bc; ///< Maintains block database. CanonBlockChain m_bc; ///< Maintains block database.
BlockQueue m_bq; ///< Maintains a list of incoming blocks not yet on the blockchain (to be imported). BlockQueue m_bq; ///< Maintains a list of incoming blocks not yet on the blockchain (to be imported).
@ -304,8 +327,10 @@ private:
Handler m_tqReady; Handler m_tqReady;
Handler m_bqReady; 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_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_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? bool m_paranoia = false; ///< Should we be paranoid about our state?
mutable std::chrono::system_clock::time_point m_lastGarbageCollection; mutable std::chrono::system_clock::time_point m_lastGarbageCollection;
@ -319,6 +344,8 @@ private:
Mutex x_signalled; Mutex x_signalled;
std::atomic<bool> m_syncTransactionQueue = {false}; std::atomic<bool> m_syncTransactionQueue = {false};
std::atomic<bool> m_syncBlockQueue = {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 ClientBase::logs(LogFilter const& _f) const
{ {
LocalisedLogEntries ret; LocalisedLogEntries ret;
unsigned begin = min<unsigned>(bc().number() + 1, (unsigned)_f.latest()); unsigned begin = min(bc().number() + 1, (unsigned)numberFromHash(_f.latest()));
unsigned end = min(bc().number(), min(begin, (unsigned)_f.earliest())); unsigned end = min(bc().number(), min(begin, (unsigned)numberFromHash(_f.earliest())));
// Handle pending transactions differently as they're not on the block chain. // Handle pending transactions differently as they're not on the block chain.
if (begin > bc().number()) if (begin > bc().number())
@ -197,11 +197,10 @@ LocalisedLogEntries ClientBase::logs(LogFilter const& _f) const
{ {
// Might have a transaction that contains a matching log. // Might have a transaction that contains a matching log.
TransactionReceipt const& tr = temp.receipt(i); TransactionReceipt const& tr = temp.receipt(i);
auto th = temp.pending()[i].sha3();
LogEntries le = _f.matches(tr); LogEntries le = _f.matches(tr);
if (le.size()) if (le.size())
for (unsigned j = 0; j < le.size(); ++j) for (unsigned j = 0; j < le.size(); ++j)
ret.insert(ret.begin(), LocalisedLogEntry(le[j], begin, th)); ret.insert(ret.begin(), LocalisedLogEntry(le[j]));
} }
begin = bc().number(); begin = bc().number();
} }
@ -216,20 +215,22 @@ LocalisedLogEntries ClientBase::logs(LogFilter const& _f) const
{ {
int total = 0; int total = 0;
auto h = bc().numberHash(n); auto h = bc().numberHash(n);
auto info = bc().info(h);
auto receipts = bc().receipts(h).receipts; auto receipts = bc().receipts(h).receipts;
unsigned logIndex = 0;
for (size_t i = 0; i < receipts.size(); i++) for (size_t i = 0; i < receipts.size(); i++)
{ {
logIndex++;
TransactionReceipt receipt = receipts[i]; TransactionReceipt receipt = receipts[i];
if (_f.matches(receipt.bloom())) if (_f.matches(receipt.bloom()))
{ {
auto info = bc().info(h);
auto th = transaction(info.hash(), i).sha3(); auto th = transaction(info.hash(), i).sha3();
LogEntries le = _f.matches(receipt); LogEntries le = _f.matches(receipt);
if (le.size()) if (le.size())
{ {
total += le.size(); total += le.size();
for (unsigned j = 0; j < le.size(); ++j) for (unsigned j = 0; j < le.size(); ++j)
ret.insert(ret.begin(), LocalisedLogEntry(le[j], n, th)); ret.insert(ret.begin(), LocalisedLogEntry(le[j], info, th, i, logIndex));
} }
} }
@ -328,6 +329,8 @@ LocalisedLogEntries ClientBase::checkWatch(unsigned _watchId)
BlockInfo ClientBase::blockInfo(h256 _hash) const BlockInfo ClientBase::blockInfo(h256 _hash) const
{ {
if (_hash == PendingBlockHash)
return preMine().info();
return BlockInfo(bc().block(_hash)); return BlockInfo(bc().block(_hash));
} }
@ -415,17 +418,16 @@ h256s ClientBase::pendingHashes() const
return h256s() + postMine().pendingHashes(); return h256s() + postMine().pendingHashes();
} }
StateDiff ClientBase::diff(unsigned _txi, h256 _block) const StateDiff ClientBase::diff(unsigned _txi, h256 _block) const
{ {
State st = asOf(_block); 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 StateDiff ClientBase::diff(unsigned _txi, BlockNumber _block) const
{ {
State st = asOf(_block); 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 Addresses ClientBase::addresses(BlockNumber _block) const
@ -457,6 +459,24 @@ h256 ClientBase::hashFromNumber(BlockNumber _number) const
BlockNumber ClientBase::numberFromHash(h256 _blockHash) const BlockNumber ClientBase::numberFromHash(h256 _blockHash) const
{ {
if (_blockHash == PendingBlockHash)
return bc().number() + 1;
else if (_blockHash == LatestBlockHash)
return bc().number();
else if (_blockHash == EarliestBlockHash)
return 0;
return bc().number(_blockHash); return bc().number(_blockHash);
} }
int ClientBase::compareBlockHashes(h256 _h1, h256 _h2) const
{
BlockNumber n1 = numberFromHash(_h1);
BlockNumber n2 = numberFromHash(_h2);
if (n1 > n2) {
return 1;
} else if (n1 == n2) {
return 0;
}
return -1;
}

13
libethereum/ClientBase.h

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

11
libethereum/CommonNet.h

@ -38,9 +38,9 @@ namespace eth
#if ETH_DEBUG #if ETH_DEBUG
static const unsigned c_maxHashes = 2048; ///< Maximum number of hashes BlockHashes will ever send. 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_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. static const unsigned c_maxPayload = 262144; ///< Maximum size of packet for us to send.
#else #else
static const unsigned c_maxHashes = 2048; ///< Maximum number of hashes BlockHashes will ever send. static const unsigned c_maxHashes = 2048; ///< Maximum number of hashes BlockHashes will ever send.
@ -84,5 +84,12 @@ enum class Syncing
Done 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); WriteGuard l(m_man->x_subs);
m_asked = RangeMask<unsigned>(m_man->m_chainStart, m_man->m_chainStart + m_man->m_chainCount); 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); m_man->m_subs.insert(this);
} }
@ -98,7 +97,6 @@ void HashDownloadSub::resetFetch()
Guard l(m_fetch); Guard l(m_fetch);
m_remaining = 0; m_remaining = 0;
m_asked = RangeMask<unsigned>(m_man->m_chainStart, m_man->m_chainStart + m_man->m_chainCount); 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) unsigned HashDownloadSub::nextFetch(unsigned _n)
@ -110,10 +108,9 @@ unsigned HashDownloadSub::nextFetch(unsigned _n)
if (!m_man || m_man->chainEmpty()) if (!m_man || m_man->chainEmpty())
return 0; return 0;
m_asked = (~(m_man->taken() + m_attempted)).lowest(_n); m_asked = (~(m_man->taken())).lowest(_n);
if (m_asked.empty()) if (m_asked.empty())
m_asked = (~(m_man->taken(true) + m_attempted)).lowest(_n); m_asked = (~(m_man->taken(true))).lowest(_n);
m_attempted += m_asked;
return *m_asked.begin(); 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); } 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& asked() const { return m_asked; }
RangeMask<unsigned> const& attemped() const { return m_attempted; }
private: private:
void resetFetch(); // Called by DownloadMan when we need to reset the download. void resetFetch(); // Called by DownloadMan when we need to reset the download.
@ -196,7 +195,6 @@ private:
mutable Mutex m_fetch; mutable Mutex m_fetch;
unsigned m_remaining; unsigned m_remaining;
RangeMask<unsigned> m_asked; RangeMask<unsigned> m_asked;
RangeMask<unsigned> m_attempted;
}; };
class HashDownloadMan class HashDownloadMan
@ -255,6 +253,11 @@ public:
return m_got.full(); return m_got.full();
} }
unsigned gotCount() const
{
return m_got.size();
}
size_t chainSize() const { ReadGuard l(m_lock); return m_chainCount; } size_t chainSize() const { ReadGuard l(m_lock); return m_chainCount; }
size_t chainEmpty() const { ReadGuard l(m_lock); return m_chainCount == 0; } 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); } 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; using namespace p2p;
unsigned const EthereumHost::c_oldProtocolVersion = 60; //TODO: remove this once v61+ is common unsigned const EthereumHost::c_oldProtocolVersion = 60; //TODO: remove this once v61+ is common
unsigned const c_chainReorgSize = 30000;
EthereumHost::EthereumHost(BlockChain const& _ch, TransactionQueue& _tq, BlockQueue& _bq, u256 _networkId): EthereumHost::EthereumHost(BlockChain const& _ch, TransactionQueue& _tq, BlockQueue& _bq, u256 _networkId):
HostCapability<EthereumPeer>(), HostCapability<EthereumPeer>(),
@ -50,6 +51,7 @@ EthereumHost::EthereumHost(BlockChain const& _ch, TransactionQueue& _tq, BlockQu
{ {
m_latestBlockSent = _ch.currentHash(); m_latestBlockSent = _ch.currentHash();
m_hashMan.reset(m_chain.number() + 1); m_hashMan.reset(m_chain.number() + 1);
m_bqRoomAvailable = m_bq.onRoomAvailable([this](){ m_continueSync = true; });
} }
EthereumHost::~EthereumHost() EthereumHost::~EthereumHost()
@ -91,7 +93,7 @@ void EthereumHost::doWork()
bool netChange = ensureInitialised(); bool netChange = ensureInitialised();
auto h = m_chain.currentHash(); 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 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) if (m_newTransactions)
{ {
@ -105,6 +107,12 @@ void EthereumHost::doWork()
} }
} }
if (m_continueSync)
{
m_continueSync = false;
continueSync();
}
foreachPeer([](EthereumPeer* _p) { _p->tick(); }); foreachPeer([](EthereumPeer* _p) { _p->tick(); });
// return netChange; // return netChange;
@ -144,6 +152,7 @@ void EthereumHost::maintainTransactions()
RLPStream ts; RLPStream ts;
_p->prep(ts, TransactionsPacket, n).appendRaw(b, n); _p->prep(ts, TransactionsPacket, n).appendRaw(b, n);
_p->sealAndSend(ts); _p->sealAndSend(ts);
cnote << "Sent" << n << "transactions to " << _p->session()->info().clientVersion;
} }
_p->m_requireTransactions = false; _p->m_requireTransactions = false;
}); });
@ -237,7 +246,7 @@ void EthereumHost::maintainBlocks(h256 const& _currentHash)
void EthereumHost::onPeerStatus(EthereumPeer* _peer) void EthereumHost::onPeerStatus(EthereumPeer* _peer)
{ {
Guard l(x_sync); RecursiveGuard l(x_sync);
if (_peer->m_genesisHash != m_chain.genesisHash()) if (_peer->m_genesisHash != m_chain.genesisHash())
_peer->disable("Invalid genesis hash"); _peer->disable("Invalid genesis hash");
else if (_peer->m_protocolVersion != protocolVersion() && _peer->m_protocolVersion != c_oldProtocolVersion) 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."); _peer->disable("Peer banned for previous bad behaviour.");
else else
{ {
if (_peer->m_protocolVersion != protocolVersion()) unsigned estimatedHashes = estimateHashes();
estimatePeerHashes(_peer); if (_peer->m_protocolVersion == protocolVersion())
else if (_peer->m_latestBlockNumber > m_chain.number()) {
_peer->m_expectedHashes = (unsigned)_peer->m_latestBlockNumber - m_chain.number() + 1000; 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 else
_peer->m_expectedHashes = 1000; _peer->m_expectedHashes = estimatedHashes;
if (m_hashMan.chainSize() < _peer->m_expectedHashes)
m_hashMan.resetToRange(m_chain.number() + 1, _peer->m_expectedHashes);
continueSync(_peer); continueSync(_peer);
} }
} }
void EthereumHost::estimatePeerHashes(EthereumPeer* _peer) unsigned EthereumHost::estimateHashes()
{ {
BlockInfo block = m_chain.info(); BlockInfo block = m_chain.info();
time_t lastBlockTime = (block.hash() == m_chain.genesisHash()) ? 1428192000 : (time_t)block.timestamp; time_t lastBlockTime = (block.hash() == m_chain.genesisHash()) ? 1428192000 : (time_t)block.timestamp;
time_t now = time(0); time_t now = time(0);
unsigned blockCount = 1000; unsigned blockCount = c_chainReorgSize;
if (lastBlockTime > now) if (lastBlockTime > now)
clog(NetWarn) << "Clock skew? Latest block is in the future"; clog(NetWarn) << "Clock skew? Latest block is in the future";
else else
blockCount += (now - lastBlockTime) / (unsigned)c_durationLimit; blockCount += (now - lastBlockTime) / (unsigned)c_durationLimit;
clog(NetAllDetail) << "Estimated hashes: " << blockCount; clog(NetAllDetail) << "Estimated hashes: " << blockCount;
_peer->m_expectedHashes = blockCount; return blockCount;
} }
void EthereumHost::onPeerHashes(EthereumPeer* _peer, h256s const& _hashes) void EthereumHost::onPeerHashes(EthereumPeer* _peer, h256s const& _hashes)
{ {
Guard l(x_sync); RecursiveGuard l(x_sync);
assert(_peer->m_asking == Asking::Nothing); if (_peer->m_syncHashNumber > 0)
_peer->m_syncHashNumber += _hashes.size();
_peer->setAsking(Asking::Nothing);
onPeerHashes(_peer, _hashes, false); onPeerHashes(_peer, _hashes, false);
} }
@ -287,13 +303,23 @@ void EthereumHost::onPeerHashes(EthereumPeer* _peer, h256s const& _hashes, bool
{ {
if (_hashes.empty()) if (_hashes.empty())
{ {
onPeerDoneHashes(_peer, true); _peer->m_hashSub.doneFetch();
continueSync();
return; return;
} }
bool syncByNumber = _peer->m_syncHashNumber;
if (!syncByNumber && !_complete && _peer->m_syncHash != m_syncingLatestHash)
{
// Obsolete hashes, discard
continueSync(_peer);
return;
}
unsigned knowns = 0; unsigned knowns = 0;
unsigned unknowns = 0; unsigned unknowns = 0;
h256s neededBlocks; h256s neededBlocks;
bool syncByNumber = !m_syncingLatestHash; unsigned firstNumber = _peer->m_syncHashNumber - _hashes.size();
for (unsigned i = 0; i < _hashes.size(); ++i) for (unsigned i = 0; i < _hashes.size(); ++i)
{ {
_peer->addRating(1); _peer->addRating(1);
@ -323,8 +349,11 @@ void EthereumHost::onPeerHashes(EthereumPeer* _peer, h256s const& _hashes, bool
} }
else else
knowns++; knowns++;
if (!syncByNumber) if (!syncByNumber)
m_syncingLatestHash = h; m_syncingLatestHash = h;
else
_peer->m_hashSub.noteHash(firstNumber + i, 1);
} }
if (syncByNumber) if (syncByNumber)
{ {
@ -339,7 +368,7 @@ void EthereumHost::onPeerHashes(EthereumPeer* _peer, h256s const& _hashes, bool
if (_complete) if (_complete)
{ {
m_needSyncBlocks = true; m_needSyncBlocks = true;
continueSync(_peer); continueSync();
} }
else if (syncByNumber && m_hashMan.isComplete()) else if (syncByNumber && m_hashMan.isComplete())
{ {
@ -370,14 +399,15 @@ void EthereumHost::onPeerDoneHashes(EthereumPeer* _peer, bool _localChain)
{ {
m_man.resetToChain(m_hashes); m_man.resetToChain(m_hashes);
m_hashes.clear(); m_hashes.clear();
m_hashMan.reset(m_chain.number() + 1);
} }
continueSync(); continueSync();
} }
void EthereumHost::onPeerBlocks(EthereumPeer* _peer, RLP const& _r) void EthereumHost::onPeerBlocks(EthereumPeer* _peer, RLP const& _r)
{ {
Guard l(x_sync); RecursiveGuard l(x_sync);
assert(_peer->m_asking == Asking::Nothing); _peer->setAsking(Asking::Nothing);
unsigned itemCount = _r.itemCount(); unsigned itemCount = _r.itemCount();
clog(NetMessageSummary) << "Blocks (" << dec << itemCount << "entries)" << (itemCount ? "" : ": NoMoreBlocks"); clog(NetMessageSummary) << "Blocks (" << dec << itemCount << "entries)" << (itemCount ? "" : ": NoMoreBlocks");
@ -386,6 +416,7 @@ void EthereumHost::onPeerBlocks(EthereumPeer* _peer, RLP const& _r)
// Got to this peer's latest block - just give up. // Got to this peer's latest block - just give up.
clog(NetNote) << "Finishing blocks fetch..."; clog(NetNote) << "Finishing blocks fetch...";
// NOTE: need to notify of giving up on chain-hashes, too, altering state as necessary. // NOTE: need to notify of giving up on chain-hashes, too, altering state as necessary.
_peer->m_sub.doneFetch();
_peer->setIdle(); _peer->setIdle();
return; 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."; 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); continueSync(_peer);
} }
void EthereumHost::onPeerNewHashes(EthereumPeer* _peer, h256s const& _hashes) void EthereumHost::onPeerNewHashes(EthereumPeer* _peer, h256s const& _hashes)
{ {
Guard l(x_sync); RecursiveGuard l(x_sync);
if (isSyncing_UNSAFE()) if (isSyncing_UNSAFE() || _peer->isConversing())
{ {
clog(NetMessageSummary) << "Ignoring new hashes since we're already downloading."; clog(NetMessageSummary) << "Ignoring new hashes since we're already downloading.";
return; return;
} }
clog(NetNote) << "New block hash discovered: syncing without help."; clog(NetNote) << "New block hash discovered: syncing without help.";
_peer->m_syncHashNumber = 0;
onPeerHashes(_peer, _hashes, true); onPeerHashes(_peer, _hashes, true);
} }
void EthereumHost::onPeerNewBlock(EthereumPeer* _peer, RLP const& _r) void EthereumHost::onPeerNewBlock(EthereumPeer* _peer, RLP const& _r)
{ {
Guard l(x_sync); RecursiveGuard l(x_sync);
if (isSyncing_UNSAFE()) if (isSyncing_UNSAFE() || _peer->isConversing())
{ {
clog(NetMessageSummary) << "Ignoring new blocks since we're already downloading."; clog(NetMessageSummary) << "Ignoring new blocks since we're already downloading.";
return; return;
@ -519,7 +541,7 @@ void EthereumHost::onPeerNewBlock(EthereumPeer* _peer, RLP const& _r)
_peer->m_knownBlocks.insert(h); _peer->m_knownBlocks.insert(h);
if (sync) 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() void EthereumHost::continueSync()
{ {
clog(NetAllDetail) << "Getting help with downloading hashes and blocks"; clog(NetAllDetail) << "Continuing sync for all peers";
foreachPeer([&](EthereumPeer* _p) foreachPeer([&](EthereumPeer* _p)
{ {
if (_p->m_asking == Asking::Nothing) if (_p->m_asking == Asking::Nothing)
@ -564,22 +598,43 @@ void EthereumHost::continueSync()
void EthereumHost::continueSync(EthereumPeer* _peer) void EthereumHost::continueSync(EthereumPeer* _peer)
{ {
assert(_peer->m_asking == Asking::Nothing); assert(_peer->m_asking == Asking::Nothing);
bool otherPeerSync = false; bool otherPeerV60Sync = false;
if (m_needSyncHashes && peerShouldGrabChain(_peer)) bool otherPeerV61Sync = false;
if (m_needSyncHashes)
{ {
if (!peerShouldGrabChain(_peer))
{
_peer->setIdle();
return;
}
foreachPeer([&](EthereumPeer* _p) foreachPeer([&](EthereumPeer* _p)
{ {
if (_p != _peer && _p->m_asking == Asking::Hashes && _p->m_protocolVersion != protocolVersion()) if (_p != _peer && _p->m_asking == Asking::Hashes)
otherPeerSync = true; // Already have a peer downloading hash chain with old protocol, do nothing {
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(); _peer->setIdle();
return; 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 _peer->requestHashes(); /// v61+ and not catching up to a particular hash
}
else else
{ {
// Restart/continue sync in single peer mode // Restart/continue sync in single peer mode
@ -588,17 +643,63 @@ void EthereumHost::continueSync(EthereumPeer* _peer)
m_syncingLatestHash =_peer->m_latestHash; m_syncingLatestHash =_peer->m_latestHash;
m_syncingTotalDifficulty = _peer->m_totalDifficulty; 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 else
_peer->setIdle(); _peer->setIdle();
} }
bool EthereumHost::peerCanHelp(EthereumPeer* _peer) const
{
(void)_peer;
return true;
}
bool EthereumHost::peerShouldGrabBlocks(EthereumPeer* _peer) const 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 td = _peer->m_totalDifficulty;
auto lh = m_syncingLatestHash; auto lh = m_syncingLatestHash;
auto ctd = m_chain.details().totalDifficulty; auto ctd = m_chain.details().totalDifficulty;
@ -611,6 +712,10 @@ bool EthereumHost::peerShouldGrabBlocks(EthereumPeer* _peer) const
bool EthereumHost::peerShouldGrabChain(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(); h256 c = m_chain.currentHash();
unsigned n = m_chain.number(); unsigned n = m_chain.number();
u256 td = m_chain.details().totalDifficulty; u256 td = m_chain.details().totalDifficulty;
@ -630,13 +735,14 @@ bool EthereumHost::peerShouldGrabChain(EthereumPeer* _peer) const
bool EthereumHost::isSyncing_UNSAFE() const bool EthereumHost::isSyncing_UNSAFE() const
{ {
/// We need actual peer information here to handle the case when we are the first ever peer on the network to mine. return m_needSyncBlocks || m_needSyncHashes;
/// I.e. on a new private network the first node mining has noone to sync with and should start block propogation immediately.
bool syncing = false;
foreachPeer([&](EthereumPeer* _p)
{
if (_p->m_asking != Asking::Nothing)
syncing = true;
});
return syncing;
} }
HashChainStatus EthereumHost::status()
{
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(); void reset();
DownloadMan const& downloadMan() const { return m_man; } DownloadMan const& downloadMan() const { return m_man; }
bool isSyncing() const { Guard l(x_sync); return isSyncing_UNSAFE(); } bool isSyncing() const { RecursiveGuard l(x_sync); return isSyncing_UNSAFE(); }
bool isBanned(p2p::NodeId _id) const { return !!m_banned.count(_id); } bool isBanned(p2p::NodeId const& _id) const { return !!m_banned.count(_id); }
void noteNewTransactions() { m_newTransactions = true; } void noteNewTransactions() { m_newTransactions = true; }
void noteNewBlocks() { m_newBlocks = 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 onPeerNewHashes(EthereumPeer* _peer, h256s const& _hashes); ///< Called by peer once it has new hashes
void onPeerHashes(EthereumPeer* _peer, h256s const& _hashes); ///< Called by peer once it has another sequential block of hashes during sync void onPeerHashes(EthereumPeer* _peer, 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 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; } DownloadMan& downloadMan() { return m_man; }
HashDownloadMan& hashDownloadMan() { return m_hashMan; } HashDownloadMan& hashDownloadMan() { return m_hashMan; }
BlockChain const& chain() { return m_chain; } BlockChain const& chain() { return m_chain; }
HashChainStatus status();
static unsigned const c_oldProtocolVersion; static unsigned const c_oldProtocolVersion;
@ -122,11 +124,14 @@ private:
void onPeerHashes(EthereumPeer* _peer, h256s const& _hashes, bool _complete); void onPeerHashes(EthereumPeer* _peer, h256s const& _hashes, bool _complete);
bool peerShouldGrabBlocks(EthereumPeer* _peer) const; bool peerShouldGrabBlocks(EthereumPeer* _peer) const;
bool peerShouldGrabChain(EthereumPeer* _peer) const; bool peerShouldGrabChain(EthereumPeer* _peer) const;
bool peerCanHelp(EthereumPeer* _peer) const;
unsigned estimateHashes();
void estimatePeerHashes(EthereumPeer* _peer); void estimatePeerHashes(EthereumPeer* _peer);
BlockChain const& m_chain; BlockChain const& m_chain;
TransactionQueue& m_tq; ///< Maintains a list of incoming transactions not yet in a block on the blockchain. TransactionQueue& m_tq; ///< Maintains a list of incoming transactions not yet in a block on the blockchain.
BlockQueue& m_bq; ///< Maintains a list of incoming blocks not yet on the blockchain (to be imported). BlockQueue& m_bq; ///< Maintains a list of incoming blocks not yet on the blockchain (to be imported).
Handler m_bqRoomAvailable;
u256 m_networkId; u256 m_networkId;
@ -141,12 +146,15 @@ private:
bool m_newTransactions = false; bool m_newTransactions = false;
bool m_newBlocks = 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_needSyncHashes = true; ///< Indicates if need to downlad hashes
bool m_needSyncBlocks = true; ///< Indicates if we still need to download some blocks 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. 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. 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 <libdevcore/Common.h>
#include <libethcore/Exceptions.h> #include <libethcore/Exceptions.h>
#include <libp2p/Session.h> #include <libp2p/Session.h>
#include <libp2p/Host.h>
#include "BlockChain.h" #include "BlockChain.h"
#include "EthereumHost.h" #include "EthereumHost.h"
#include "TransactionQueue.h" #include "TransactionQueue.h"
@ -40,6 +41,7 @@ EthereumPeer::EthereumPeer(Session* _s, HostCapabilityFace* _h, unsigned _i, Cap
m_hashSub(host()->hashDownloadMan()), m_hashSub(host()->hashDownloadMan()),
m_peerCapabilityVersion(_cap.second) m_peerCapabilityVersion(_cap.second)
{ {
session()->addNote("manners", isRude() ? "RUDE" : "nice");
m_syncHashNumber = host()->chain().number() + 1; m_syncHashNumber = host()->chain().number() + 1;
requestStatus(); requestStatus();
} }
@ -50,10 +52,20 @@ EthereumPeer::~EthereumPeer()
abortSync(); abortSync();
} }
bool EthereumPeer::isRude() const
{
return repMan().isRude(*session(), name());
}
void EthereumPeer::setRude()
{
repMan().noteRude(*session(), name());
session()->addNote("manners", "RUDE");
}
void EthereumPeer::abortSync() void EthereumPeer::abortSync()
{ {
if (isSyncing()) host()->onPeerAborting(this);
setIdle();
} }
EthereumHost* EthereumPeer::host() const EthereumHost* EthereumPeer::host() const
@ -105,6 +117,7 @@ void EthereumPeer::requestHashes()
{ {
assert(m_asking == Asking::Nothing); assert(m_asking == Asking::Nothing);
m_syncHashNumber = m_hashSub.nextFetch(c_maxHashesAsk); m_syncHashNumber = m_hashSub.nextFetch(c_maxHashesAsk);
m_syncHash = h256();
setAsking(Asking::Hashes); setAsking(Asking::Hashes);
RLPStream s; RLPStream s;
prep(s, GetBlockHashesByNumberPacket, 2) << m_syncHashNumber << c_maxHashesAsk; prep(s, GetBlockHashesByNumberPacket, 2) << m_syncHashNumber << c_maxHashesAsk;
@ -119,13 +132,15 @@ void EthereumPeer::requestHashes(h256 const& _lastHash)
RLPStream s; RLPStream s;
prep(s, GetBlockHashesPacket, 2) << _lastHash << c_maxHashesAsk; prep(s, GetBlockHashesPacket, 2) << _lastHash << c_maxHashesAsk;
clog(NetMessageDetail) << "Requesting block hashes staring from " << _lastHash; clog(NetMessageDetail) << "Requesting block hashes staring from " << _lastHash;
m_syncHash = _lastHash;
m_syncHashNumber = 0;
sealAndSend(s); sealAndSend(s);
} }
void EthereumPeer::requestBlocks() void EthereumPeer::requestBlocks()
{ {
setAsking(Asking::Blocks); setAsking(Asking::Blocks);
auto blocks = m_sub.nextFetch(c_maxBlocksAsk); auto blocks = m_sub.nextFetch(isRude() ? 1 : c_maxBlocksAsk);
if (blocks.size()) if (blocks.size())
{ {
RLPStream s; RLPStream s;
@ -145,7 +160,7 @@ void EthereumPeer::setAsking(Asking _a)
m_lastAsk = chrono::system_clock::now(); 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("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() void EthereumPeer::tick()
@ -155,11 +170,16 @@ void EthereumPeer::tick()
session()->disconnect(PingTimeout); session()->disconnect(PingTimeout);
} }
bool EthereumPeer::isSyncing() const bool EthereumPeer::isConversing() const
{ {
return m_asking != Asking::Nothing; 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) bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
{ {
try try
@ -175,8 +195,16 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
m_genesisHash = _r[4].toHash<h256>(); m_genesisHash = _r[4].toHash<h256>();
if (m_peerCapabilityVersion == host()->protocolVersion()) if (m_peerCapabilityVersion == host()->protocolVersion())
{ {
m_protocolVersion = host()->protocolVersion(); if (_r.itemCount() != 6)
m_latestBlockNumber = _r[5].toInt<u256>(); {
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; 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."; clog(NetWarn) << "Peer giving us hashes when we didn't ask for them.";
break; break;
} }
setAsking(Asking::Nothing);
h256s hashes(itemCount); h256s hashes(itemCount);
for (unsigned i = 0; i < itemCount; ++i) for (unsigned i = 0; i < itemCount; ++i)
{
hashes[i] = _r[i].toHash<h256>(); hashes[i] = _r[i].toHash<h256>();
m_hashSub.noteHash(m_syncHashNumber + i, 1);
}
m_syncHashNumber += itemCount;
host()->onPeerHashes(this, hashes); host()->onPeerHashes(this, hashes);
break; break;
} }
@ -288,10 +311,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
if (m_asking != Asking::Blocks) if (m_asking != Asking::Blocks)
clog(NetImpolite) << "Peer giving us blocks when we didn't ask for them."; clog(NetImpolite) << "Peer giving us blocks when we didn't ask for them.";
else else
{
setAsking(Asking::Nothing);
host()->onPeerBlocks(this, _r); host()->onPeerBlocks(this, _r);
}
break; break;
} }
case NewBlockPacket: case NewBlockPacket:

20
libethereum/EthereumPeer.h

@ -82,6 +82,12 @@ public:
/// Request blocks. Uses block download manager. /// Request blocks. Uses block download manager.
void requestBlocks(); void requestBlocks();
/// Check if this node is rude.
bool isRude() const;
/// Set that it's a rude node.
void setRude();
private: private:
using p2p::Capability::sealAndSend; using p2p::Capability::sealAndSend;
@ -101,10 +107,13 @@ private:
void setAsking(Asking _g); void setAsking(Asking _g);
/// Do we presently need syncing with this peer? /// 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? /// Are we presently in a critical part of the syncing process with this peer?
bool isSyncing() const; bool isCriticalSyncing() const;
/// Runs period checks to check up on the peer. /// Runs period checks to check up on the peer.
void tick(); 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 /// 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. /// 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_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_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. /// Once we're asking for blocks, this becomes in use.
DownloadSub m_sub; DownloadSub m_sub;

141
libethereum/Executive.cpp

@ -19,6 +19,9 @@
#include "Executive.h" #include "Executive.h"
#include <boost/timer.hpp> #include <boost/timer.hpp>
#if ETH_JSONRPC || !ETH_TRUE
#include <json/json.h>
#endif
#include <libdevcore/CommonIO.h> #include <libdevcore/CommonIO.h>
#include <libevm/VMFactory.h> #include <libevm/VMFactory.h>
#include <libevm/VM.h> #include <libevm/VM.h>
@ -34,6 +37,111 @@ using namespace dev::eth;
const char* VMTraceChannel::name() { return "EVM"; } const char* VMTraceChannel::name() { return "EVM"; }
const char* ExecutiveWarnChannel::name() { return WarnChannel::name(); } 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): Executive::Executive(State& _s, BlockChain const& _bc, unsigned _level):
m_s(_s), m_s(_s),
m_lastHashes(_bc.lastHashes((unsigned)_s.info().number - 1)), m_lastHashes(_bc.lastHashes((unsigned)_s.info().number - 1)),
@ -68,7 +176,7 @@ void Executive::initialize(Transaction const& _transaction)
if (!m_t.checkPayment()) if (!m_t.checkPayment())
{ {
clog(ExecutiveWarnChannel) << "Not enough gas to pay for the transaction: Require >" << m_t.gasRequired() << " Got" << m_t.gas(); clog(ExecutiveWarnChannel) << "Not enough gas to pay for the transaction: Require >" << m_t.gasRequired() << " Got" << m_t.gas();
m_excepted = TransactionException::OutOfGas; m_excepted = TransactionException::OutOfGasBase;
BOOST_THROW_EXCEPTION(OutOfGasBase() << RequirementError(m_t.gasRequired(), (bigint)m_t.gas())); 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. // Avoid unaffordable transactions.
m_gasCost = (bigint)m_t.gas() * m_t.gasPrice(); m_gasCost = (bigint)m_t.gas() * m_t.gasPrice();
m_totalCost = m_t.value() + m_gasCost; bigint totalCost = m_t.value() + m_gasCost;
if (m_s.balance(m_t.sender()) < m_totalCost) if (m_s.balance(m_t.sender()) < totalCost)
{ {
clog(ExecutiveWarnChannel) << "Not enough cash: Require >" << m_totalCost << " Got" << m_s.balance(m_t.sender()) << "for sender: " << m_t.sender(); clog(ExecutiveWarnChannel) << "Not enough cash: Require >" << totalCost << " Got" << m_s.balance(m_t.sender()) << "for sender: " << m_t.sender();
m_excepted = TransactionException::NotEnoughCash; m_excepted = TransactionException::NotEnoughCash;
BOOST_THROW_EXCEPTION(NotEnoughCash() << RequirementError(m_totalCost, (bigint)m_s.balance(m_t.sender())) << errinfo_comment(m_t.sender().abridged())); BOOST_THROW_EXCEPTION(NotEnoughCash() << RequirementError(totalCost, (bigint)m_s.balance(m_t.sender())) << errinfo_comment(m_t.sender().abridged()));
} }
} }
@ -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() m_outRef = _p.out; // Save ref to expected output buffer to be used in go()
bytes const& c = m_s.code(_p.codeAddress); 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. // Execute _init.
if (!_init.empty()) 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.m_cache[m_newAddress] = Account(m_s.balance(m_newAddress), Account::ContractConception);
m_s.transferBalance(_sender, m_newAddress, _endowment); 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) bool Executive::go(OnOpFunc const& _onOp)
{ {
if (m_ext) if (m_ext)

27
libethereum/Executive.h

@ -25,6 +25,11 @@
#include <libevm/VMFace.h> #include <libevm/VMFace.h>
#include "Transaction.h" #include "Transaction.h"
namespace Json
{
class Value;
}
namespace dev namespace dev
{ {
namespace eth namespace eth
@ -38,6 +43,22 @@ struct Manifest;
struct VMTraceChannel: public LogChannel { static const char* name(); static const int verbosity = 11; }; 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; }; 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. * @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) {} Executive(State& _s, LastHashes const& _lh, unsigned _level = 0): m_s(_s), m_lastHashes(_lh), m_depth(_level) {}
/// Basic constructor. /// Basic constructor.
Executive(State& _s, BlockChain const& _bc, unsigned _level = 0); Executive(State& _s, BlockChain const& _bc, unsigned _level = 0);
/// Basic destructor.
~Executive() = default;
Executive(Executive const&) = delete; Executive(Executive const&) = delete;
void operator=(Executive) = delete; void operator=(Executive) = delete;
@ -124,7 +143,7 @@ public:
private: private:
State& m_s; ///< The state to which this operation/transaction is applied. State& m_s; ///< The state to which this operation/transaction is applied.
LastHashes m_lastHashes; LastHashes m_lastHashes;
std::shared_ptr<ExtVM> m_ext; ///< The VM externality object for the VM execution or null if no VM is required. std::shared_ptr<ExtVM> m_ext; ///< The VM externality object for the VM execution or null if no VM is required. shared_ptr used only to allow ExtVM forward reference.
bytesRef m_outRef; ///< Reference to "expected output" buffer. bytesRef m_outRef; ///< Reference to "expected output" buffer.
ExecutionResult* m_res = nullptr; ///< Optional storage for execution results. ExecutionResult* m_res = nullptr; ///< Optional storage for execution results.
Address m_newAddress; ///< The address of the created contract in the case of create() being called. Address m_newAddress; ///< The address of the created contract in the case of create() being called.
@ -137,9 +156,7 @@ private:
Transaction m_t; ///< The original transaction. Set by setup(). Transaction m_t; ///< The original transaction. Set by setup().
LogEntries m_logs; ///< The log entries created by this transaction. Set by finalize(). LogEntries m_logs; ///< The log entries created by this transaction. Set by finalize().
bigint m_gasRequired; ///< Gas required during execution of the transaction.
bigint m_gasCost; bigint m_gasCost;
bigint m_totalCost;
}; };
} }

4
libethereum/ExtVM.h

@ -39,8 +39,8 @@ class ExtVM: public ExtVMFace
{ {
public: public:
/// Full constructor. /// 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): 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(), _s.m_previousBlock, _s.m_currentBlock, _lh, _depth), m_s(_s), m_origCache(_s.m_cache) 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); 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 std::pair<h256, unsigned> transactionLocation(h256 const& _transactionHash) const = 0;
virtual h256 hashFromNumber(BlockNumber _number) const = 0; virtual h256 hashFromNumber(BlockNumber _number) const = 0;
virtual BlockNumber numberFromHash(h256 _blockHash) const = 0; virtual BlockNumber numberFromHash(h256 _blockHash) const = 0;
virtual int compareBlockHashes(h256 _h1, h256 _h2) const = 0;
virtual BlockInfo blockInfo(h256 _hash) const = 0; virtual BlockInfo blockInfo(h256 _hash) const = 0;
virtual BlockDetails blockDetails(h256 _hash) const = 0; virtual BlockDetails blockDetails(h256 _hash) const = 0;
@ -196,6 +197,8 @@ public:
virtual void stopMining() = 0; virtual void stopMining() = 0;
/// Are we mining now? /// Are we mining now?
virtual bool isMining() const = 0; virtual bool isMining() const = 0;
/// Would we like to mine now?
virtual bool wouldMine() const = 0;
/// Current hash rate. /// Current hash rate.
virtual uint64_t hashrate() const = 0; virtual uint64_t hashrate() const = 0;

27
libethereum/LogFilter.cpp

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

15
libethereum/LogFilter.h

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

8
libethereum/Precompiled.cpp

@ -55,9 +55,13 @@ void ecrecoverCode(bytesConstRef _in, bytesRef _out)
{ {
try 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. // 2. Enact the block's transactions onto this state.
m_ourAddress = bi.coinbaseAddress; m_ourAddress = bi.coinbaseAddress;
enact(&b, _bc, _ir); enact(BlockChain::verifyBlock(b), _bc, _ir);
} }
else else
{ {
@ -158,6 +158,7 @@ State::State(State const& _s):
m_transactions(_s.m_transactions), m_transactions(_s.m_transactions),
m_receipts(_s.m_receipts), m_receipts(_s.m_receipts),
m_transactionSet(_s.m_transactionSet), m_transactionSet(_s.m_transactionSet),
m_touched(_s.m_touched),
m_cache(_s.m_cache), m_cache(_s.m_cache),
m_previousBlock(_s.m_previousBlock), m_previousBlock(_s.m_previousBlock),
m_currentBlock(_s.m_currentBlock), 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; 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 trie = SecureTrieDB<Address, OverlayDB>(const_cast<OverlayDB*>(&m_db), rootHash());
auto trieD = SecureTrieDB<Address, OverlayDB>(const_cast<OverlayDB*>(&_c.m_db), _c.rootHash()); auto trieD = SecureTrieDB<Address, OverlayDB>(const_cast<OverlayDB*>(&_c.m_db), _c.rootHash());
for (auto i: trie) if (_quick)
ads.insert(i.first), trieAds.insert(i.first); {
for (auto i: trieD) trieAds = m_touched;
ads.insert(i.first), trieAdsD.insert(i.first); trieAdsD = _c.m_touched;
for (auto i: m_cache) (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); ads.insert(i.first);
for (auto i: _c.m_cache) for (auto const& i: _c.m_cache)
ads.insert(i.first); ads.insert(i.first);
// cnote << *this; // cnote << *this;
// cnote << _c; // cnote << _c;
for (auto i: ads) for (auto const& i: ads)
{ {
auto it = m_cache.find(i); auto it = m_cache.find(i);
auto itD = _c.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() 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(); m_cache.clear();
} }
@ -382,7 +393,7 @@ bool State::sync(BlockChain const& _bc, h256 _block, BlockInfo const& _bi, Impor
return ret; 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 #if ETH_TIMED_ENACTMENTS
boost::timer t; boost::timer t;
@ -393,8 +404,8 @@ u256 State::enactOn(bytesConstRef _block, BlockInfo const& _bi, BlockChain const
#endif #endif
// Check family: // Check family:
BlockInfo biParent = _bc.info(_bi.parentHash); BlockInfo biParent = _bc.info(_block.info.parentHash);
_bi.verifyParent(biParent); _block.info.verifyParent(biParent);
#if ETH_TIMED_ENACTMENTS #if ETH_TIMED_ENACTMENTS
populateVerify = t.elapsed(); populateVerify = t.elapsed();
@ -410,7 +421,7 @@ u256 State::enactOn(bytesConstRef _block, BlockInfo const& _bi, BlockChain const
t.restart(); t.restart();
#endif #endif
sync(_bc, _bi.parentHash, BlockInfo(), _ir); sync(_bc, _block.info.parentHash, BlockInfo(), _ir);
resetCurrent(); resetCurrent();
#if ETH_TIMED_ENACTMENTS #if ETH_TIMED_ENACTMENTS
@ -450,6 +461,7 @@ void State::resetCurrent()
m_receipts.clear(); m_receipts.clear();
m_transactionSet.clear(); m_transactionSet.clear();
m_cache.clear(); m_cache.clear();
m_touched.clear();
m_currentBlock = BlockInfo(); m_currentBlock = BlockInfo();
m_currentBlock.coinbaseAddress = m_ourAddress; m_currentBlock.coinbaseAddress = m_ourAddress;
m_currentBlock.timestamp = max(m_previousBlock.timestamp + 1, (u256)time(0)); 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); LastHashes lh = _bc.lastHashes((unsigned)m_previousBlock.number);
vector<bytes> receipts; vector<bytes> receipts;
ostringstream ss; string ret;
unsigned i = 0; unsigned i = 0;
for (auto const& tr: rlp[1]) for (auto const& tr: rlp[1])
{ {
ss << " VM Execution of transaction" << i << ":" << endl; StandardTrace st;
execute(lh, Transaction(tr.data(), CheckTransaction::Everything), Permanence::Committed, Executive::standardTrace(ss)); 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; RLPStream receiptRLP;
m_receipts.back().streamRLP(receiptRLP); m_receipts.back().streamRLP(receiptRLP);
receipts.push_back(receiptRLP.out()); receipts.push_back(receiptRLP.out());
++i; ++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. DEV_TIMED_FUNCTION_ABOVE(500);
BlockInfo bi(_block, (_ir & ImportRequirements::ValidNonce) ? CheckEverything : IgnoreNonce);
// m_currentBlock is assumed to be prepopulated and reset.
#if !ETH_RELEASE #if !ETH_RELEASE
assert(m_previousBlock.hash() == bi.parentHash); assert(m_previousBlock.hash() == _block.info.parentHash);
assert(m_currentBlock.parentHash == bi.parentHash); assert(m_currentBlock.parentHash == _block.info.parentHash);
assert(rootHash() == m_previousBlock.stateRoot); assert(rootHash() == m_previousBlock.stateRoot);
#endif #endif
if (m_currentBlock.parentHash != m_previousBlock.hash()) if (m_currentBlock.parentHash != m_previousBlock.hash())
// Internal client error.
BOOST_THROW_EXCEPTION(InvalidParentHash()); BOOST_THROW_EXCEPTION(InvalidParentHash());
// Populate m_currentBlock with the correct values. // Populate m_currentBlock with the correct values.
m_currentBlock = bi; m_currentBlock = _block.info;
m_currentBlock.verifyInternals(_block);
m_currentBlock.noteDirty(); m_currentBlock.noteDirty();
// cnote << "playback begins:" << m_state.root(); // cnote << "playback begins:" << m_state.root();
// cnote << m_state; // cnote << m_state;
LastHashes lh = _bc.lastHashes((unsigned)m_previousBlock.number); LastHashes lh;
RLP rlp(_block); DEV_TIMED_ABOVE(lastHashes, 500)
lh = _bc.lastHashes((unsigned)m_previousBlock.number);
RLP rlp(_block.block);
vector<bytes> receipts; vector<bytes> receipts;
// All ok with the block generally. Play back the transactions now... // All ok with the block generally. Play back the transactions now...
unsigned i = 0; unsigned i = 0;
for (auto const& tr: rlp[1]) DEV_TIMED_ABOVE(txEcec, 500)
{ for (auto const& tr: _block.transactions)
try {
LogOverride<ExecutiveWarnChannel> o(false);
execute(lh, Transaction(tr.data(), CheckTransaction::Everything));
}
catch (...)
{ {
badBlock(_block, "Invalid transaction"); try
cwarn << " Transaction Index:" << i; {
LogOverride<ExecutiveWarnChannel> o(true); LogOverride<ExecutiveWarnChannel> o(false);
execute(lh, Transaction(tr.data(), CheckTransaction::Everything)); execute(lh, tr);
throw; }
catch (Exception& ex)
{
ex << errinfo_transactionIndex(i);
throw;
}
RLPStream receiptRLP;
m_receipts.back().streamRLP(receiptRLP);
receipts.push_back(receiptRLP.out());
++i;
} }
RLPStream receiptRLP; h256 receiptsRoot;
m_receipts.back().streamRLP(receiptRLP); DEV_TIMED_ABOVE(receiptsRoot, 500)
receipts.push_back(receiptRLP.out()); receiptsRoot = orderedTrieRoot(receipts);
++i;
}
auto receiptsRoot = orderedTrieRoot(receipts);
if (receiptsRoot != m_currentBlock.receiptsRoot) if (receiptsRoot != m_currentBlock.receiptsRoot)
{ {
badBlock(_block, "Bad receipts state root"); InvalidReceiptsStateRoot ex;
cwarn << " Received: " << toString(m_currentBlock.receiptsRoot); ex << Hash256RequirementError(receiptsRoot, m_currentBlock.receiptsRoot);
cwarn << " Expected: " << toString(receiptsRoot) << " which is:"; ex << errinfo_receipts(receipts);
for (unsigned j = 0; j < i; ++j) ex << errinfo_vmtrace(vmTrace(_block.block, _bc, _ir));
{ BOOST_THROW_EXCEPTION(ex);
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());
} }
if (m_currentBlock.logBloom != logBloom()) if (m_currentBlock.logBloom != logBloom())
{ {
badBlock(_block, "Bad log bloom"); InvalidLogBloom ex;
cwarn << " Receipt blooms:"; ex << LogBloomRequirementError(logBloom(), m_currentBlock.logBloom);
for (unsigned j = 0; j < i; ++j) ex << errinfo_receipts(receipts);
{ BOOST_THROW_EXCEPTION(ex);
auto b = receipts[j];
cwarn << " " << j << ":" << TransactionReceipt(&b).bloom().hex();
}
cwarn << " Final bloom:" << m_currentBlock.logBloom.hex();
BOOST_THROW_EXCEPTION(InvalidLogBloom());
} }
// Initialise total difficulty calculation. // Initialise total difficulty calculation.
@ -680,72 +685,85 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, ImportRequirement
// Check uncles & apply their rewards to state. // Check uncles & apply their rewards to state.
if (rlp[2].itemCount() > 2) if (rlp[2].itemCount() > 2)
{ {
badBlock(_block, "Too many uncles"); TooManyUncles ex;
BOOST_THROW_EXCEPTION(TooManyUncles()); ex << errinfo_max(2);
ex << errinfo_got(rlp[2].itemCount());
BOOST_THROW_EXCEPTION(ex);
} }
vector<BlockInfo> rewarded; 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()); excluded.insert(m_currentBlock.hash());
for (auto const& i: rlp[2]) unsigned ii = 0;
{ DEV_TIMED_ABOVE(uncleCheck, 500)
auto h = sha3(i.data()); for (auto const& i: rlp[2])
if (excluded.count(h))
{ {
badBlock(_block, "Invalid uncle included"); try
BOOST_THROW_EXCEPTION(UncleInChain() << errinfo_comment("Uncle in block already mentioned") << errinfo_data(toString(excluded)) << errinfo_hash256(sha3(i.data()))); {
} auto h = sha3(i.data());
excluded.insert(h); 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 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);
// tdIncrease += uncle.difficulty; BlockInfo uncleParent;
rewarded.push_back(uncle); 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 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. // 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()) 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(); m_db.rollback();
BOOST_THROW_EXCEPTION(InvalidStateRoot()); BOOST_THROW_EXCEPTION(InvalidStateRoot() << Hash256RequirementError(rootHash(), m_currentBlock.stateRoot));
} }
if (m_currentBlock.gasUsed != gasUsed()) if (m_currentBlock.gasUsed != gasUsed())
{ {
// Rollback the trie. // Rollback the trie.
badBlock(_block, "Invalid gas used");
m_db.rollback(); m_db.rollback();
BOOST_THROW_EXCEPTION(InvalidGasUsed() << RequirementError(bigint(gasUsed()), bigint(m_currentBlock.gasUsed))); 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); paranoia("immediately before database commit", true);
// Commit the new trie to disk. // Commit the new trie to disk.
clog(StateTrace) << "Committing to disk: stateRoot" << m_currentBlock.stateRoot << "=" << rootHash() << "=" << toHex(asBytes(m_db.lookup(rootHash()))); if (isChannelVisible<StateTrace>()) // Avoid calling toHex if not needed
clog(StateTrace) << "Committing to disk: stateRoot" << m_currentBlock.stateRoot << "=" << rootHash() << "=" << toHex(asBytes(m_db.lookup(rootHash())));
try { try {
EnforceRefs er(m_db, true); EnforceRefs er(m_db, true);
@ -773,7 +792,8 @@ void State::cleanup(bool _fullCommit)
} }
m_db.commit(); m_db.commit();
clog(StateTrace) << "Committed: stateRoot" << m_currentBlock.stateRoot << "=" << rootHash() << "=" << toHex(asBytes(m_db.lookup(rootHash()))); if (isChannelVisible<StateTrace>()) // Avoid calling toHex if not needed
clog(StateTrace) << "Committed: stateRoot" << m_currentBlock.stateRoot << "=" << rootHash() << "=" << toHex(asBytes(m_db.lookup(rootHash())));
paranoia("immediately after database commit", true); paranoia("immediately after database commit", true);
m_previousBlock = m_currentBlock; m_previousBlock = m_currentBlock;
@ -823,7 +843,7 @@ bool State::amIJustParanoid(BlockChain const& _bc)
cnote << "PARANOIA root:" << s.rootHash(); cnote << "PARANOIA root:" << s.rootHash();
// s.m_currentBlock.populate(&block.out(), false); // s.m_currentBlock.populate(&block.out(), false);
// s.m_currentBlock.verifyInternals(&block.out()); // 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); s.cleanup(false);
return true; return true;
} }
@ -1122,6 +1142,8 @@ h256 State::codeHash(Address _contract) const
{ {
if (!addressHasCode(_contract)) if (!addressHasCode(_contract))
return EmptySHA3; return EmptySHA3;
if (m_cache[_contract].isFreshCode())
return sha3(code(_contract));
return m_cache[_contract].codeHash(); return m_cache[_contract].codeHash();
} }
@ -1160,6 +1182,8 @@ bool State::isTrieGood(bool _enforceRefs, bool _requireNoLeftOvers) const
return true; return true;
} }
#define ETH_VMTIMER 1
ExecutionResult State::execute(LastHashes const& _lh, Transaction const& _t, Permanence _p, OnOpFunc const& _onOp) ExecutionResult State::execute(LastHashes const& _lh, Transaction const& _t, Permanence _p, OnOpFunc const& _onOp)
{ {
#if ETH_PARANOIA #if ETH_PARANOIA
@ -1188,8 +1212,10 @@ ExecutionResult State::execute(LastHashes const& _lh, Transaction const& _t, Per
if (!e.execute()) if (!e.execute())
#if ETH_VMTRACE #if ETH_VMTRACE
{ {
(void)_onOp; if (isChannelVisible<VMTraceChannel>())
e.go(e.simpleTrace()); e.go(e.simpleTrace());
else
e.go(_onOp);
} }
#else #else
e.go(_onOp); e.go(_onOp);

33
libethereum/State.h

@ -46,8 +46,25 @@ namespace test { class ImportTest; class StateLoader; }
namespace eth 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 BlockChain;
class State; class State;
struct VerifiedBlockRef;
struct StateChat: public LogChannel { static const char* name(); static const int verbosity = 4; }; 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; }; struct StateTrace: public LogChannel { static const char* name(); static const int verbosity = 5; };
@ -294,10 +311,12 @@ public:
State fromPending(unsigned _i) const; State fromPending(unsigned _i) const;
/// @returns the StateDiff caused by the pending transaction of index @a _i. /// @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). /// @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. /// Sync our state with the block chain.
/// This basically involves wiping ourselves if we've been superceded and rebuilding from the transaction queue. /// 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. /// Execute all transactions within a given block.
/// @returns the additional total difficulty. /// @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. /// 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 /// @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. /// Execute the given block, assuming it corresponds to m_currentBlock.
/// Throws on failure. /// 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. /// Finalise the block, applying the earned rewards.
void applyRewards(std::vector<BlockInfo> const& _uncleBlockHeaders); void applyRewards(std::vector<BlockInfo> const& _uncleBlockHeaders);
@ -371,6 +390,7 @@ private:
TransactionReceipts m_receipts; ///< The corresponding list of transaction receipts. TransactionReceipts m_receipts; ///< The corresponding list of transaction receipts.
h256Hash m_transactionSet; ///< The set of transaction hashes that we've included in the state. h256Hash m_transactionSet; ///< The set of transaction hashes that we've included in the state.
OverlayDB m_lastTx; 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. 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); std::ostream& operator<<(std::ostream& _out, State const& _s);
template <class DB> 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) for (auto const& i: _cache)
if (i.second.isDirty()) 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()); _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; return _out;
} }
std::string badTransaction(bytesConstRef _tx, string const& _err) TransactionException dev::eth::toTransactionException(Exception const& _e)
{
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)
{ {
// 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)) if (!!dynamic_cast<BadInstruction const*>(&_e))
return TransactionException::BadInstruction; return TransactionException::BadInstruction;
if (!!dynamic_cast<BadJumpDestination const*>(&_e)) if (!!dynamic_cast<BadJumpDestination const*>(&_e))
@ -75,6 +71,28 @@ TransactionException dev::eth::toTransactionException(VMException const& _e)
return TransactionException::Unknown; 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) Transaction::Transaction(bytesConstRef _rlpData, CheckTransaction _checkSig)
{ {
int field = 0; int field = 0;
@ -114,7 +132,7 @@ Transaction::Transaction(bytesConstRef _rlpData, CheckTransaction _checkSig)
throw; throw;
} }
if (_checkSig >= CheckTransaction::Cheap && !checkPayment()) 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 Address const& Transaction::safeSender() const noexcept

9
libethereum/Transaction.h

@ -25,6 +25,7 @@
#include <libdevcore/SHA3.h> #include <libdevcore/SHA3.h>
#include <libethcore/Common.h> #include <libethcore/Common.h>
#include <libevmcore/Params.h> #include <libevmcore/Params.h>
namespace dev namespace dev
{ {
namespace eth namespace eth
@ -48,6 +49,8 @@ enum class TransactionException
{ {
None = 0, None = 0,
Unknown, Unknown,
BadRLP,
OutOfGasIntrinsic, ///< Too little gas to pay for the base transaction cost.
InvalidSignature, InvalidSignature,
InvalidNonce, InvalidNonce,
NotEnoughCash, NotEnoughCash,
@ -69,7 +72,8 @@ enum class CodeDeposit
struct VMException; 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. /// Description of the result of executing a transaction.
struct ExecutionResult struct ExecutionResult
@ -224,8 +228,5 @@ inline std::ostream& operator<<(std::ostream& _out, Transaction const& _t)
return _out; 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;
using namespace dev::eth; 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), myAddress(_myAddress),
caller(_caller), caller(_caller),
origin(_origin), origin(_origin),
@ -33,6 +33,7 @@ ExtVMFace::ExtVMFace(Address _myAddress, Address _caller, Address _origin, u256
gasPrice(_gasPrice), gasPrice(_gasPrice),
data(_data), data(_data),
code(_code), code(_code),
codeHash(_codeHash),
lastHashes(_lh), lastHashes(_lh),
previousBlock(_previousBlock), previousBlock(_previousBlock),
currentBlock(_currentBlock), currentBlock(_currentBlock),

29
libevm/ExtVMFace.h

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

322
libevm/VM.cpp

@ -45,165 +45,169 @@ static array<InstructionMetric, 256> metrics()
return s_ret; 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 static const auto c_metrics = metrics();
m_curPC = 0; auto& metric = c_metrics[static_cast<size_t>(_inst)];
m_jumpDests.clear();
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; }; require(metric.args, metric.ret);
auto gasForMem = [](bigint _size) -> bigint
auto onOperation = [&]()
{ {
bigint s = _size / 32; if (_onOp)
return (bigint)c_memoryGas * s + s * s / c_quadCoeffDiv; _onOp(m_steps, _inst, newTempSize > m_temp.size() ? (newTempSize - m_temp.size()) / 32 : bigint(0), runGas, io_gas, this, &_ext);
}; };
if (m_jumpDests.empty()) auto memNeed = [](u256 _offset, dev::u256 _size) { return _size ? (bigint)_offset + _size : (bigint)0; };
for (unsigned i = 0; i < _ext.code.size(); ++i)
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) runGas = c_sstoreResetGas;
m_jumpDests.push_back(i); _ext.sub.refunds += c_sstoreRefundGas;
else if (_ext.code[i] >= (byte)Instruction::PUSH1 && _ext.code[i] <= (byte)Instruction::PUSH32)
i += _ext.code[i] - (unsigned)Instruction::PUSH1 + 1;
} }
u256 nextPC = m_curPC + 1; else
for (uint64_t steps = 0; true; m_curPC = nextPC, nextPC = m_curPC + 1, ++steps) 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... unsigned n = (unsigned)_inst - (unsigned)Instruction::LOG0;
Instruction inst = (Instruction)_ext.getCode(m_curPC); runGas = c_logGas + c_logTopicGas * n + (bigint)c_logDataGas * m_stack[m_stack.size() - 2];
auto metric = c_metrics[(int)inst]; newTempSize = memNeed(m_stack[m_stack.size() - 1], m_stack[m_stack.size() - 2]);
int gasPriceTier = metric.gasPriceTier; break;
}
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;
case Instruction::SLOAD: case Instruction::CALL:
runGas = c_sloadGas; case Instruction::CALLCODE:
break; 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: auto gasForMem = [](bigint _size) -> bigint
case Instruction::MSTORE: {
newTempSize = (bigint)m_stack.back() + 32; bigint s = _size / 32;
break; return (bigint)c_memoryGas * s + s * s / c_quadCoeffDiv;
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: newTempSize = (newTempSize + 31) / 32 * 32;
runGas = 1; if (newTempSize > m_temp.size())
break; runGas += gasForMem(newTempSize) - gasForMem(m_temp.size());
runGas += c_copyGas * ((copySize + 31) / 32);
case Instruction::LOG0: onOperation();
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;
}
case Instruction::CALL: if (io_gas < runGas)
case Instruction::CALLCODE: BOOST_THROW_EXCEPTION(OutOfGas());
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: io_gas -= (u256)runGas;
{
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:;
}
newTempSize = (newTempSize + 31) / 32 * 32; if (newTempSize > m_temp.size())
if (newTempSize > m_temp.size()) m_temp.resize((size_t)newTempSize);
runGas += gasForMem(newTempSize) - gasForMem(m_temp.size()); }
runGas += c_copyGas * ((copySize + 31) / 32);
onOperation(); bytesConstRef VM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp)
{
m_stack.reserve((unsigned)c_stackLimit);
if (io_gas < runGas) for (size_t i = 0; i < _ext.code.size(); ++i)
BOOST_THROW_EXCEPTION(OutOfGas()); {
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_steps = 0;
m_temp.resize((size_t)newTempSize); 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) switch (inst)
{ {
case Instruction::ADD: case Instruction::ADD:
@ -299,7 +303,7 @@ bytesConstRef VM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp)
case Instruction::SIGNEXTEND: case Instruction::SIGNEXTEND:
if (m_stack.back() < 31) 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& number = m_stack[m_stack.size() - 2];
u256 mask = ((u256(1) << testBit) - 1); u256 mask = ((u256(1) << testBit) - 1);
if (boost::multiprecision::bit_test(number, testBit)) 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::DUP15:
case Instruction::DUP16: 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]); m_stack.push_back(m_stack[m_stack.size() - n]);
break; break;
} }
@ -501,7 +505,7 @@ bytesConstRef VM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp)
case Instruction::SWAP15: case Instruction::SWAP15:
case Instruction::SWAP16: case Instruction::SWAP16:
{ {
unsigned n = (int)inst - (int)Instruction::SWAP1 + 2; auto n = (unsigned)inst - (unsigned)Instruction::SWAP1 + 2;
auto d = m_stack.back(); auto d = m_stack.back();
m_stack.back() = m_stack[m_stack.size() - n]; m_stack.back() = m_stack[m_stack.size() - n];
m_stack[m_stack.size() - n] = d; 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(); m_stack.pop_back();
break; break;
case Instruction::JUMP: case Instruction::JUMP:
nextPC = m_stack.back(); nextPC = verifyJumpDest(m_stack.back(), m_jumpDests);
if (find(m_jumpDests.begin(), m_jumpDests.end(), (uint64_t)nextPC) == m_jumpDests.end() || nextPC > numeric_limits<uint64_t>::max() )
BOOST_THROW_EXCEPTION(BadJumpDestination());
m_stack.pop_back(); m_stack.pop_back();
break; break;
case Instruction::JUMPI: case Instruction::JUMPI:
if (m_stack[m_stack.size() - 2]) if (m_stack[m_stack.size() - 2])
{ nextPC = verifyJumpDest(m_stack.back(), m_jumpDests);
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());
}
m_stack.pop_back(); m_stack.pop_back();
m_stack.pop_back(); m_stack.pop_back();
break; break;
@ -598,7 +596,7 @@ bytesConstRef VM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp)
break; break;
case Instruction::CREATE: case Instruction::CREATE:
{ {
u256 endowment = m_stack.back(); auto endowment = m_stack.back();
m_stack.pop_back(); m_stack.pop_back();
unsigned initOff = (unsigned)m_stack.back(); unsigned initOff = (unsigned)m_stack.back();
m_stack.pop_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::CALL:
case Instruction::CALLCODE: case Instruction::CALLCODE:
{ {
if (!callParams) CallParameters callParams;
callParams.reset(new CallParameters); callParams.gas = m_stack.back();
callParams->gas = m_stack.back();
if (m_stack[m_stack.size() - 3] > 0) if (m_stack[m_stack.size() - 3] > 0)
callParams->gas += c_callStipend; callParams.gas += c_callStipend;
m_stack.pop_back(); m_stack.pop_back();
callParams->codeAddress = asAddress(m_stack.back()); callParams.codeAddress = asAddress(m_stack.back());
m_stack.pop_back(); m_stack.pop_back();
callParams->value = m_stack.back(); callParams.value = m_stack.back();
m_stack.pop_back(); m_stack.pop_back();
unsigned inOff = (unsigned)m_stack.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(); unsigned outSize = (unsigned)m_stack.back();
m_stack.pop_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.onOp = _onOp;
callParams->senderAddress = _ext.myAddress; callParams.senderAddress = _ext.myAddress;
callParams->receiveAddress = inst == Instruction::CALL ? callParams->codeAddress : callParams->senderAddress; callParams.receiveAddress = inst == Instruction::CALL ? callParams.codeAddress : callParams.senderAddress;
callParams->data = bytesConstRef(m_temp.data() + inOff, inSize); callParams.data = bytesConstRef(m_temp.data() + inOff, inSize);
callParams->out = bytesRef(m_temp.data() + outOff, outSize); callParams.out = bytesRef(m_temp.data() + outOff, outSize);
m_stack.push_back(_ext.call(*callParams)); m_stack.push_back(_ext.call(callParams));
} }
else else
m_stack.push_back(0); m_stack.push_back(0);
io_gas += callParams->gas; io_gas += callParams.gas;
break; break;
} }
case Instruction::RETURN: case Instruction::RETURN:

6
libevm/VM.h

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

13
libevm/VMFace.h

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

2
libevmasm/Assembly.cpp

@ -109,7 +109,7 @@ string Assembly::getLocationFromSources(StringMap const& _sourceCodes, SourceLoc
if (newLinePos != string::npos) if (newLinePos != string::npos)
cut = cut.substr(0, newLinePos) + "..."; cut = cut.substr(0, newLinePos) + "...";
return move(cut); return cut;
} }
ostream& Assembly::streamAsm(ostream& _out, string const& _prefix, StringMap const& _sourceCodes) const 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(); 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> initialStackContents;
map<int, Id> targetStackContents; map<int, Id> targetStackContents;
int minHeight = m_state.stackHeight() + 1; int minHeight = m_state.stackHeight() + 1;
@ -52,15 +65,7 @@ vector<AssemblyItem> CommonSubexpressionEliminator::getOptimizedItems()
targetStackContents targetStackContents
); );
if (m_breakingItem) if (m_breakingItem)
{
items.push_back(*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; return items;
} }

4
libevmcore/Exceptions.h

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

2
libjsqrc/ethereumjs/.jshintrc

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

7
libjsqrc/ethereumjs/.versions

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

5
libjsqrc/ethereumjs/bower.json

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

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

File diff suppressed because one or more lines are too long

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

File diff suppressed because one or more lines are too long

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

File diff suppressed because one or more lines are too long

1879
libjsqrc/ethereumjs/dist/web3.js

File diff suppressed because one or more lines are too long

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

File diff suppressed because one or more lines are too long

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

File diff suppressed because one or more lines are too long

23
libjsqrc/ethereumjs/example/contract.html

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

29
libjsqrc/ethereumjs/example/event_inc.html

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

203
libjsqrc/ethereumjs/example/icap.html

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

102
libjsqrc/ethereumjs/example/namereg.html

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

76
libjsqrc/ethereumjs/example/natspec_contract.html

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

2
libjsqrc/ethereumjs/index.js

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

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

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

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

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

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

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

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

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

2
libjsqrc/ethereumjs/lib/version.json

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

13
libjsqrc/ethereumjs/lib/web3.js

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

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

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

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

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

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

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

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

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

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

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

2
libjsqrc/ethereumjs/package.js

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

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

Loading…
Cancel
Save