Browse Source

Merge branch 'develop' into rlpx

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

7
CMakeLists.txt

@ -37,7 +37,7 @@ option(ETHKEY "Build the CLI key manager component" ON)
option(SOLIDITY "Build the Solidity language components" ON) option(SOLIDITY "Build the Solidity language components" ON)
option(SERPENT "Build the Serpent language components" ON) option(SERPENT "Build the Serpent language components" ON)
option(TOOLS "Build the tools components" ON) option(TOOLS "Build the tools components" ON)
option(NCURSES "Build the NCurses components" ON) option(NCURSES "Build the NCurses components" OFF)
option(GUI "Build GUI components (AlethZero, Mix)" ON) option(GUI "Build GUI components (AlethZero, Mix)" ON)
option(TESTS "Build the tests." ON) option(TESTS "Build the tests." ON)
option(EVMJIT "Build just-in-time compiler for EVM code (requires LLVM)" OFF) option(EVMJIT "Build just-in-time compiler for EVM code (requires LLVM)" OFF)
@ -222,7 +222,7 @@ elseif (BUNDLE STREQUAL "full")
set(SOLIDITY ON) set(SOLIDITY ON)
set(USENPM ON) set(USENPM ON)
set(GUI ON) set(GUI ON)
set(NCURSES ${DECENT_PLATFORM}) # set(NCURSES ${DECENT_PLATFORM})
set(TOOLS ON) set(TOOLS ON)
set(TESTS ON) set(TESTS ON)
set(FATDB ON) set(FATDB ON)
@ -249,7 +249,7 @@ elseif (BUNDLE STREQUAL "user")
set(SOLIDITY OFF) set(SOLIDITY OFF)
set(USENPM OFF) set(USENPM OFF)
set(GUI ON) set(GUI ON)
set(NCURSES ${DECENT_PLATFORM}) # set(NCURSES ${DECENT_PLATFORM})
set(TOOLS ON) set(TOOLS ON)
set(TESTS OFF) set(TESTS OFF)
elseif (BUNDLE STREQUAL "wallet") elseif (BUNDLE STREQUAL "wallet")
@ -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")

1
alethzero/DappLoader.cpp

@ -108,6 +108,7 @@ void DappLoader::downloadComplete(QNetworkReply* _reply)
//inject web3 js //inject web3 js
QByteArray content = "<script>\n"; QByteArray content = "<script>\n";
content.append(web3Content()); content.append(web3Content());
content.append(("web3.admin.setSessionKey('" + m_sessionKey + "');").c_str());
content.append("</script>\n"); content.append("</script>\n");
content.append(_reply->readAll()); content.append(_reply->readAll());
QString contentType = _reply->header(QNetworkRequest::ContentTypeHeader).toString(); QString contentType = _reply->header(QNetworkRequest::ContentTypeHeader).toString();

3
alethzero/DappLoader.h

@ -78,6 +78,8 @@ public:
///@param _uri Page Uri ///@param _uri Page Uri
void loadPage(QString const& _uri); void loadPage(QString const& _uri);
void setSessionKey(std::string const& _s) { m_sessionKey = _s; }
signals: signals:
void dappReady(Dapp& _dapp); void dappReady(Dapp& _dapp);
void pageReady(QByteArray const& _content, QString const& _mimeType, QUrl const& _uri); void pageReady(QByteArray const& _content, QString const& _mimeType, QUrl const& _uri);
@ -99,5 +101,6 @@ private:
std::set<QUrl> m_pageUrls; std::set<QUrl> m_pageUrls;
QByteArray m_web3Js; QByteArray m_web3Js;
dev::Address m_nameReg; dev::Address m_nameReg;
std::string m_sessionKey;
}; };

9
alethzero/Main.ui

@ -44,7 +44,14 @@
<string>0 bytes used</string> <string>0 bytes used</string>
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QLabel" name="syncStatus">
<property name="text">
<string></string>
</property>
</widget>
</item>
<item> <item>
<widget class="QLabel" name="chainStatus"> <widget class="QLabel" name="chainStatus">
<property name="text"> <property name="text">

26
alethzero/MainWin.cpp

@ -198,6 +198,7 @@ Main::Main(QWidget *parent) :
statusBar()->addPermanentWidget(ui->balance); statusBar()->addPermanentWidget(ui->balance);
statusBar()->addPermanentWidget(ui->peerCount); statusBar()->addPermanentWidget(ui->peerCount);
statusBar()->addPermanentWidget(ui->mineStatus); statusBar()->addPermanentWidget(ui->mineStatus);
statusBar()->addPermanentWidget(ui->syncStatus);
statusBar()->addPermanentWidget(ui->chainStatus); statusBar()->addPermanentWidget(ui->chainStatus);
statusBar()->addPermanentWidget(ui->blockCount); statusBar()->addPermanentWidget(ui->blockCount);
@ -209,7 +210,9 @@ Main::Main(QWidget *parent) :
m_webThree.reset(new WebThreeDirect(string("AlethZero/v") + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), getDataDir(), WithExisting::Trust, {"eth"/*, "shh"*/}, p2p::NetworkPreferences(), network)); m_webThree.reset(new WebThreeDirect(string("AlethZero/v") + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), getDataDir(), WithExisting::Trust, {"eth"/*, "shh"*/}, p2p::NetworkPreferences(), network));
m_httpConnector.reset(new jsonrpc::HttpServer(SensibleHttpPort, "", "", dev::SensibleHttpThreads)); m_httpConnector.reset(new jsonrpc::HttpServer(SensibleHttpPort, "", "", dev::SensibleHttpThreads));
m_server.reset(new OurWebThreeStubServer(*m_httpConnector, *web3(), this)); auto w3ss = new OurWebThreeStubServer(*m_httpConnector, this);
m_server.reset(w3ss);
auto sessionKey = w3ss->newSession({true});
connect(&*m_server, SIGNAL(onNewId(QString)), SLOT(addNewId(QString))); connect(&*m_server, SIGNAL(onNewId(QString)), SLOT(addNewId(QString)));
m_server->setIdentities(keysAsVector(owned())); m_server->setIdentities(keysAsVector(owned()));
m_server->StartListening(); m_server->StartListening();
@ -226,12 +229,16 @@ Main::Main(QWidget *parent) :
m_dappHost.reset(new DappHost(8081)); m_dappHost.reset(new DappHost(8081));
m_dappLoader = new DappLoader(this, web3(), getNameReg()); m_dappLoader = new DappLoader(this, web3(), getNameReg());
m_dappLoader->setSessionKey(sessionKey);
connect(m_dappLoader, &DappLoader::dappReady, this, &Main::dappLoaded); connect(m_dappLoader, &DappLoader::dappReady, this, &Main::dappLoaded);
connect(m_dappLoader, &DappLoader::pageReady, this, &Main::pageLoaded); connect(m_dappLoader, &DappLoader::pageReady, this, &Main::pageLoaded);
// ui->webView->page()->settings()->setAttribute(QWebEngineSettings::DeveloperExtrasEnabled, true); // ui->webView->page()->settings()->setAttribute(QWebEngineSettings::DeveloperExtrasEnabled, true);
// QWebEngineInspector* inspector = new QWebEngineInspector(); // QWebEngineInspector* inspector = new QWebEngineInspector();
// inspector->setPage(page); // inspector->setPage(page);
setBeneficiary(*m_keyManager.accounts().begin()); setBeneficiary(*m_keyManager.accounts().begin());
ethereum()->setDefault(LatestBlock);
readSettings(); readSettings();
m_transact = new Transact(this, this); m_transact = new Transact(this, this);
@ -240,6 +247,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 +816,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,9 +1252,15 @@ void Main::refreshBlockCount()
{ {
auto d = ethereum()->blockChain().details(); auto d = ethereum()->blockChain().details();
BlockQueueStatus b = ethereum()->blockQueueStatus(); BlockQueueStatus b = ethereum()->blockQueueStatus();
HashChainStatus h = ethereum()->hashChainStatus(); SyncStatus sync = ethereum()->syncStatus();
ui->chainStatus->setText(QString("%9/%10%11 hashes %3 ready %4 verifying %5 unverified %6 future %7 unknown %8 bad %1 #%2") QString syncStatus = EthereumHost::stateName(sync.state);
.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).arg(h.received).arg(h.estimated ? "~" : "").arg(h.total)); if (sync.state == SyncState::HashesParallel || sync.state == SyncState::HashesSingle)
syncStatus += QString(": %1/%2%3").arg(sync.hashesReceived).arg(sync.hashesEstimated ? "~" : "").arg(sync.hashesTotal);
if (sync.state == SyncState::Blocks || sync.state == SyncState::NewBlocks)
syncStatus += QString(": %1/%2").arg(sync.blocksReceived).arg(sync.blocksTotal);
ui->syncStatus->setText(syncStatus);
ui->chainStatus->setText(QString("%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));
} }
void Main::on_turboMining_triggered() void Main::on_turboMining_triggered()

15
alethzero/OurWebThreeStubServer.cpp

@ -31,10 +31,9 @@ using namespace dev::eth;
OurWebThreeStubServer::OurWebThreeStubServer( OurWebThreeStubServer::OurWebThreeStubServer(
jsonrpc::AbstractServerConnector& _conn, jsonrpc::AbstractServerConnector& _conn,
WebThreeDirect& _web3,
Main* _main Main* _main
): ):
WebThreeStubServer(_conn, _web3, make_shared<OurAccountHolder>(_web3, _main), _main->owned().toVector().toStdVector()), WebThreeStubServer(_conn, *_main->web3(), make_shared<OurAccountHolder>(_main), _main->owned().toVector().toStdVector(), _main->keyManager()),
m_main(_main) m_main(_main)
{ {
} }
@ -46,12 +45,8 @@ string OurWebThreeStubServer::shh_newIdentity()
return toJS(kp.pub()); return toJS(kp.pub());
} }
OurAccountHolder::OurAccountHolder( OurAccountHolder::OurAccountHolder(Main* _main):
WebThreeDirect& _web3, AccountHolder([=](){ return _main->ethereum(); }),
Main* _main
):
AccountHolder([=](){ return m_web3->ethereum(); }),
m_web3(&_web3),
m_main(_main) m_main(_main)
{ {
connect(_main, SIGNAL(poll()), this, SLOT(doValidations())); connect(_main, SIGNAL(poll()), this, SLOT(doValidations()));
@ -135,7 +130,7 @@ void OurAccountHolder::doValidations()
else else
// sign and submit. // sign and submit.
if (Secret s = m_main->retrieveSecret(t.from)) if (Secret s = m_main->retrieveSecret(t.from))
m_web3->ethereum()->submitTransaction(s, t); m_main->ethereum()->submitTransaction(s, t);
} }
} }
@ -155,7 +150,7 @@ bool OurAccountHolder::validateTransaction(TransactionSkeleton const& _t, bool _
return showCreationNotice(_t, _toProxy); return showCreationNotice(_t, _toProxy);
} }
h256 contractCodeHash = m_web3->ethereum()->postState().codeHash(_t.to); h256 contractCodeHash = m_main->ethereum()->postState().codeHash(_t.to);
if (contractCodeHash == EmptySHA3) if (contractCodeHash == EmptySHA3)
{ {
// recipient has no code - nothing special about this transaction, show basic value transfer info // recipient has no code - nothing special about this transaction, show basic value transfer info

7
alethzero/OurWebThreeStubServer.h

@ -34,10 +34,7 @@ class OurAccountHolder: public QObject, public dev::eth::AccountHolder
Q_OBJECT Q_OBJECT
public: public:
OurAccountHolder( OurAccountHolder(Main* _main);
dev::WebThreeDirect& _web3,
Main* _main
);
public slots: public slots:
void doValidations(); void doValidations();
@ -59,7 +56,6 @@ private:
std::queue<dev::eth::TransactionSkeleton> m_queued; std::queue<dev::eth::TransactionSkeleton> m_queued;
dev::Mutex x_queued; dev::Mutex x_queued;
dev::WebThreeDirect* m_web3;
Main* m_main; Main* m_main;
}; };
@ -70,7 +66,6 @@ class OurWebThreeStubServer: public QObject, public WebThreeStubServer
public: public:
OurWebThreeStubServer( OurWebThreeStubServer(
jsonrpc::AbstractServerConnector& _conn, jsonrpc::AbstractServerConnector& _conn,
dev::WebThreeDirect& _web3,
Main* main Main* main
); );

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")

127
eth/main.cpp

@ -93,6 +93,7 @@ void interactiveHelp()
<< " accounts Gives information on all owned accounts (balances, mining beneficiary and default signer)." << endl << " accounts Gives information on all owned accounts (balances, mining beneficiary and default signer)." << endl
<< " newaccount <name> Creates a new account with the given name." << endl << " newaccount <name> Creates a new account with the given name." << endl
<< " transact Execute a given transaction." << endl << " transact Execute a given transaction." << endl
<< " transactnonce Execute a given transaction with a specified nonce." << endl
<< " txcreate Execute a given contract creation transaction." << endl << " txcreate Execute a given contract creation transaction." << endl
<< " send Execute a given transaction with current secret." << endl << " send Execute a given transaction with current secret." << endl
<< " contract Create a new contract with current secret." << endl << " contract Create a new contract with current secret." << endl
@ -108,6 +109,7 @@ void interactiveHelp()
<< " exportconfig <path> Export the config (.RLP) to the path provided." << endl << " exportconfig <path> Export the config (.RLP) to the path provided." << endl
<< " importconfig <path> Import the config (.RLP) from the path provided." << endl << " importconfig <path> Import the config (.RLP) from the path provided." << endl
<< " inspect <contract> Dumps a contract to <APPDATA>/<contract>.evm." << endl << " inspect <contract> Dumps a contract to <APPDATA>/<contract>.evm." << endl
<< " reprocess <block> Reprocess a given block." << endl
<< " dumptrace <block> <index> <filename> <format> Dumps a transaction trace" << endl << "to <filename>. <format> should be one of pretty, standard, standard+." << endl << " dumptrace <block> <index> <filename> <format> Dumps a transaction trace" << endl << "to <filename>. <format> should be one of pretty, standard, standard+." << endl
<< " dumpreceipt <block> <index> Dumps a transation receipt." << endl << " dumpreceipt <block> <index> Dumps a transation receipt." << endl
<< " exit Exits the application." << endl; << " exit Exits the application." << endl;
@ -124,6 +126,7 @@ void help()
#if ETH_JSONRPC || !ETH_TRUE #if ETH_JSONRPC || !ETH_TRUE
<< " -j,--json-rpc Enable JSON-RPC server (default: off)." << endl << " -j,--json-rpc Enable JSON-RPC server (default: off)." << endl
<< " --json-rpc-port <n> Specify JSON-RPC server port (implies '-j', default: " << SensibleHttpPort << ")." << endl << " --json-rpc-port <n> Specify JSON-RPC server port (implies '-j', default: " << SensibleHttpPort << ")." << endl
<< " --admin <password> Specify admin session key for JSON-RPC (default: auto-generated and printed at startup)." << endl
#endif #endif
<< " -K,--kill First kill the blockchain." << endl << " -K,--kill First kill the blockchain." << endl
<< " -R,--rebuild Rebuild the blockchain from the existing database." << endl << " -R,--rebuild Rebuild the blockchain from the existing database." << endl
@ -286,6 +289,7 @@ int main(int argc, char** argv)
#if ETH_JSONRPC #if ETH_JSONRPC
int jsonrpc = -1; int jsonrpc = -1;
#endif #endif
string jsonAdmin;
bool upnp = true; bool upnp = true;
WithExisting killChain = WithExisting::Trust; WithExisting killChain = WithExisting::Trust;
bool jit = false; bool jit = false;
@ -597,6 +601,8 @@ int main(int argc, char** argv)
jsonrpc = jsonrpc == -1 ? SensibleHttpPort : jsonrpc; jsonrpc = jsonrpc == -1 ? SensibleHttpPort : jsonrpc;
else if (arg == "--json-rpc-port" && i + 1 < argc) else if (arg == "--json-rpc-port" && i + 1 < argc)
jsonrpc = atoi(argv[++i]); jsonrpc = atoi(argv[++i]);
else if (arg == "--json-admin" && i + 1 < argc)
jsonAdmin = argv[++i];
#endif #endif
#if ETH_JSCONSOLE #if ETH_JSCONSOLE
else if (arg == "--console") else if (arg == "--console")
@ -679,7 +685,7 @@ int main(int argc, char** argv)
VMFactory::setKind(jit ? VMKind::JIT : VMKind::Interpreter); VMFactory::setKind(jit ? VMKind::JIT : VMKind::Interpreter);
auto netPrefs = publicIP.empty() ? NetworkPreferences(listenIP ,listenPort, upnp) : NetworkPreferences(publicIP, listenIP ,listenPort, upnp); auto netPrefs = publicIP.empty() ? NetworkPreferences(listenIP ,listenPort, upnp) : NetworkPreferences(publicIP, listenIP ,listenPort, upnp);
auto nodesState = contents((dbPath.size() ? dbPath : getDataDir()) + "/network.rlp"); auto nodesState = contents((dbPath.size() ? dbPath : getDataDir()) + "/network.rlp");
std::string clientImplString = "++eth/" + clientName + "v" + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM) + (jit ? "/JIT" : ""); std::string clientImplString = "++eth/" + clientName + "v" + dev::Version + "-" + string(DEV_QUOTED(ETH_COMMIT_HASH)).substr(0, 8) + (ETH_CLEAN_REPO ? "" : "*") + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM) + (jit ? "/JIT" : "");
dev::WebThreeDirect web3( dev::WebThreeDirect web3(
clientImplString, clientImplString,
dbPath, dbPath,
@ -806,14 +812,14 @@ int main(int argc, char** argv)
cout << "Transaction Signer: " << signingKey << endl; cout << "Transaction Signer: " << signingKey << endl;
cout << "Mining Benefactor: " << beneficiary << endl; cout << "Mining Benefactor: " << beneficiary << endl;
web3.startNetwork();
cout << "Node ID: " << web3.enode() << endl;
if (bootstrap) if (bootstrap || !remoteHost.empty())
for (auto const& i: Host::pocHosts()) {
web3.requirePeer(i.first, i.second); web3.startNetwork();
if (remoteHost.size()) cout << "Node ID: " << web3.enode() << endl;
web3.addNode(p2p::NodeId(), remoteHost + ":" + toString(remotePort)); }
else
cout << "Networking disabled. To start, use netstart or pass -b or a remote host." << endl;
#if ETH_JSONRPC || !ETH_TRUE #if ETH_JSONRPC || !ETH_TRUE
shared_ptr<WebThreeStubServer> jsonrpcServer; shared_ptr<WebThreeStubServer> jsonrpcServer;
@ -821,11 +827,22 @@ int main(int argc, char** argv)
if (jsonrpc > -1) if (jsonrpc > -1)
{ {
jsonrpcConnector = unique_ptr<jsonrpc::AbstractServerConnector>(new jsonrpc::HttpServer(jsonrpc, "", "", SensibleHttpThreads)); jsonrpcConnector = unique_ptr<jsonrpc::AbstractServerConnector>(new jsonrpc::HttpServer(jsonrpc, "", "", SensibleHttpThreads));
jsonrpcServer = shared_ptr<WebThreeStubServer>(new WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared<SimpleAccountHolder>([&](){return web3.ethereum();}, getAccountPassword, keyManager), vector<KeyPair>())); jsonrpcServer = shared_ptr<WebThreeStubServer>(new WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared<SimpleAccountHolder>([&](){return web3.ethereum();}, getAccountPassword, keyManager), vector<KeyPair>(), keyManager));
jsonrpcServer->StartListening(); jsonrpcServer->StartListening();
if (jsonAdmin.empty())
jsonAdmin = jsonrpcServer->newSession(SessionPermissions{true});
else
jsonrpcServer->addSession(jsonAdmin, SessionPermissions{true});
cout << "JSONRPC Admin Session Key: " << jsonAdmin << endl;
} }
#endif #endif
if (bootstrap)
for (auto const& i: Host::pocHosts())
web3.requirePeer(i.first, i.second);
if (!remoteHost.empty())
web3.addNode(p2p::NodeId(), remoteHost + ":" + toString(remotePort));
signal(SIGABRT, &sighandler); signal(SIGABRT, &sighandler);
signal(SIGTERM, &sighandler); signal(SIGTERM, &sighandler);
signal(SIGINT, &sighandler); signal(SIGINT, &sighandler);
@ -965,8 +982,13 @@ int main(int argc, char** argv)
if (jsonrpc < 0) if (jsonrpc < 0)
jsonrpc = SensibleHttpPort; jsonrpc = SensibleHttpPort;
jsonrpcConnector = unique_ptr<jsonrpc::AbstractServerConnector>(new jsonrpc::HttpServer(jsonrpc, "", "", SensibleHttpThreads)); jsonrpcConnector = unique_ptr<jsonrpc::AbstractServerConnector>(new jsonrpc::HttpServer(jsonrpc, "", "", SensibleHttpThreads));
jsonrpcServer = shared_ptr<WebThreeStubServer>(new WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared<SimpleAccountHolder>([&](){return web3.ethereum();}, getAccountPassword, keyManager), vector<KeyPair>())); jsonrpcServer = shared_ptr<WebThreeStubServer>(new WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared<SimpleAccountHolder>([&](){ return web3.ethereum(); }, getAccountPassword, keyManager), vector<KeyPair>(), keyManager));
jsonrpcServer->StartListening(); jsonrpcServer->StartListening();
if (jsonAdmin.empty())
jsonAdmin = jsonrpcServer->newSession(SessionPermissions{true});
else
jsonrpcServer->addSession(jsonAdmin, SessionPermissions{true});
cout << "JSONRPC Admin Session Key: " << jsonAdmin << endl;
} }
else if (cmd == "jsonstop") else if (cmd == "jsonstop")
{ {
@ -1175,6 +1197,75 @@ int main(int argc, char** argv)
else else
cwarn << "Require parameters: submitTransaction ADDRESS AMOUNT GASPRICE GAS SECRET DATA"; cwarn << "Require parameters: submitTransaction ADDRESS AMOUNT GASPRICE GAS SECRET DATA";
} }
else if (c && cmd == "transactnonce")
{
auto const& bc =c->blockChain();
auto h = bc.currentHash();
auto blockData = bc.block(h);
BlockInfo info(blockData);
if (iss.peek() != -1)
{
string hexAddr;
u256 amount;
u256 gasPrice;
u256 gas;
string sechex;
string sdata;
u256 nonce;
iss >> hexAddr >> amount >> gasPrice >> gas >> sechex >> sdata >> nonce;
if (!gasPrice)
gasPrice = gasPricer->bid(priority);
cnote << "Data:";
cnote << sdata;
bytes data = dev::eth::parseData(sdata);
cnote << "Bytes:";
string sbd = asString(data);
bytes bbd = asBytes(sbd);
stringstream ssbd;
ssbd << bbd;
cnote << ssbd.str();
int ssize = sechex.length();
int size = hexAddr.length();
u256 minGas = (u256)Transaction::gasRequired(data, 0);
if (size < 40)
{
if (size > 0)
cwarn << "Invalid address length:" << size;
}
else if (gas < minGas)
cwarn << "Minimum gas amount is" << minGas;
else if (ssize < 40)
{
if (ssize > 0)
cwarn << "Invalid secret length:" << ssize;
}
else
{
try
{
Secret secret = h256(fromHex(sechex));
Address dest = h160(fromHex(hexAddr));
c->submitTransaction(secret, amount, dest, data, gas, gasPrice, nonce);
}
catch (BadHexCharacter& _e)
{
cwarn << "invalid hex character, transaction rejected";
cwarn << boost::diagnostic_information(_e);
}
catch (...)
{
cwarn << "transaction rejected";
}
}
}
else
cwarn << "Require parameters: submitTransaction ADDRESS AMOUNT GASPRICE GAS SECRET DATA NONCE";
}
else if (c && cmd == "txcreate") else if (c && cmd == "txcreate")
{ {
auto const& bc =c->blockChain(); auto const& bc =c->blockChain();
@ -1403,6 +1494,22 @@ int main(int argc, char** argv)
cout << "Hex: " << toHex(rb) << endl; cout << "Hex: " << toHex(rb) << endl;
cout << r << endl; cout << r << endl;
} }
else if (c && cmd == "reprocess")
{
string block;
iss >> block;
h256 blockHash;
try
{
if (block.size() == 64 || block.size() == 66)
blockHash = h256(block);
else
blockHash = c->blockChain().numberHash(stoi(block));
c->state(blockHash);
}
catch (...)
{}
}
else if (c && cmd == "dumptrace") else if (c && cmd == "dumptrace")
{ {
unsigned block; unsigned block;

4
ethminer/CMakeLists.txt

@ -22,6 +22,10 @@ target_link_libraries(${EXECUTABLE} ${Boost_REGEX_LIBRARIES})
if (JSONRPC) if (JSONRPC)
target_link_libraries(${EXECUTABLE} ${JSON_RPC_CPP_CLIENT_LIBRARIES}) 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() endif()
target_link_libraries(${EXECUTABLE} ethcore) target_link_libraries(${EXECUTABLE} ethcore)

125
ethminer/MinerAux.h

@ -30,6 +30,7 @@
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/trim_all.hpp> #include <boost/algorithm/string/trim_all.hpp>
#include <boost/optional.hpp>
#include <libdevcore/FileSystem.h> #include <libdevcore/FileSystem.h>
#include <libevmcore/Instruction.h> #include <libevmcore/Instruction.h>
@ -97,11 +98,11 @@ public:
if ((arg == "-F" || arg == "--farm") && i + 1 < argc) if ((arg == "-F" || arg == "--farm") && i + 1 < argc)
{ {
mode = OperationMode::Farm; mode = OperationMode::Farm;
farmURL = argv[++i]; m_farmURL = argv[++i];
} }
else if (arg == "--farm-recheck" && i + 1 < argc) else if (arg == "--farm-recheck" && i + 1 < argc)
try { try {
farmRecheckPeriod = stol(argv[++i]); m_farmRecheckPeriod = stol(argv[++i]);
} }
catch (...) catch (...)
{ {
@ -110,7 +111,7 @@ public:
} }
else if (arg == "--opencl-platform" && i + 1 < argc) else if (arg == "--opencl-platform" && i + 1 < argc)
try { try {
openclPlatform = stol(argv[++i]); m_openclPlatform = stol(argv[++i]);
} }
catch (...) catch (...)
{ {
@ -119,8 +120,8 @@ public:
} }
else if (arg == "--opencl-device" && i + 1 < argc) else if (arg == "--opencl-device" && i + 1 < argc)
try { try {
openclDevice = stol(argv[++i]); m_openclDevice = stol(argv[++i]);
miningThreads = 1; m_miningThreads = 1;
} }
catch (...) catch (...)
{ {
@ -128,21 +129,20 @@ public:
throw BadArgument(); throw BadArgument();
} }
else if (arg == "--list-devices") else if (arg == "--list-devices")
{ m_shouldListDevices = true;
ProofOfWork::GPUMiner::listDevices(); else if (arg == "--allow-opencl-cpu")
exit(0); m_clAllowCPU = true;
} else if (arg == "--cl-extragpu-mem" && i + 1 < argc)
else if (arg == "--use-chunks") m_extraGPUMemory = 1000000 * stol(argv[++i]);
{ else if (arg == "--force-single-chunk")
dagChunks = 4; m_forceSingleChunk = true;
}
else if (arg == "--phone-home" && i + 1 < argc) else if (arg == "--phone-home" && i + 1 < argc)
{ {
string m = argv[++i]; string m = argv[++i];
if (isTrue(m)) if (isTrue(m))
phoneHome = true; m_phoneHome = true;
else if (isFalse(m)) else if (isFalse(m))
phoneHome = false; m_phoneHome = false;
else else
{ {
cerr << "Bad " << arg << " option: " << m << endl; cerr << "Bad " << arg << " option: " << m << endl;
@ -151,7 +151,7 @@ public:
} }
else if (arg == "--benchmark-warmup" && i + 1 < argc) else if (arg == "--benchmark-warmup" && i + 1 < argc)
try { try {
benchmarkWarmup = stol(argv[++i]); m_benchmarkWarmup = stol(argv[++i]);
} }
catch (...) catch (...)
{ {
@ -160,7 +160,7 @@ public:
} }
else if (arg == "--benchmark-trial" && i + 1 < argc) else if (arg == "--benchmark-trial" && i + 1 < argc)
try { try {
benchmarkTrial = stol(argv[++i]); m_benchmarkTrial = stol(argv[++i]);
} }
catch (...) catch (...)
{ {
@ -169,7 +169,7 @@ public:
} }
else if (arg == "--benchmark-trials" && i + 1 < argc) else if (arg == "--benchmark-trials" && i + 1 < argc)
try { try {
benchmarkTrials = stol(argv[++i]); m_benchmarkTrials = stol(argv[++i]);
} }
catch (...) catch (...)
{ {
@ -179,21 +179,12 @@ public:
else if (arg == "-C" || arg == "--cpu") else if (arg == "-C" || arg == "--cpu")
m_minerType = MinerType::CPU; m_minerType = MinerType::CPU;
else if (arg == "-G" || arg == "--opencl") else if (arg == "-G" || arg == "--opencl")
{ m_minerType = MinerType::GPU;
if (!ProofOfWork::GPUMiner::haveSufficientMemory()) else if (arg == "--current-block" && i + 1 < argc)
{ m_currentBlock = stol(argv[++i]);
cout << "No GPU device with sufficient memory was found. Defaulting to CPU" << endl;
m_minerType = MinerType::CPU;
}
else
{
m_minerType = MinerType::GPU;
miningThreads = 1;
}
}
else if (arg == "--no-precompute") else if (arg == "--no-precompute")
{ {
precompute = false; m_precompute = false;
} }
else if ((arg == "-D" || arg == "--create-dag") && i + 1 < argc) else if ((arg == "-D" || arg == "--create-dag") && i + 1 < argc)
{ {
@ -201,7 +192,7 @@ public:
mode = OperationMode::DAGInit; mode = OperationMode::DAGInit;
try try
{ {
initDAG = stol(m); m_initDAG = stol(m);
} }
catch (...) catch (...)
{ {
@ -251,7 +242,7 @@ public:
else if ((arg == "-t" || arg == "--mining-threads") && i + 1 < argc) else if ((arg == "-t" || arg == "--mining-threads") && i + 1 < argc)
{ {
try { try {
miningThreads = stol(argv[++i]); m_miningThreads = stol(argv[++i]);
} }
catch (...) catch (...)
{ {
@ -266,21 +257,36 @@ public:
void execute() void execute()
{ {
if (m_shouldListDevices)
{
ProofOfWork::GPUMiner::listDevices();
exit(0);
}
if (m_minerType == MinerType::CPU) if (m_minerType == MinerType::CPU)
ProofOfWork::CPUMiner::setNumInstances(miningThreads); ProofOfWork::CPUMiner::setNumInstances(m_miningThreads);
else if (m_minerType == MinerType::GPU) else if (m_minerType == MinerType::GPU)
{ {
ProofOfWork::GPUMiner::setDefaultPlatform(openclPlatform); ProofOfWork::GPUMiner::setNumInstances(m_miningThreads);
ProofOfWork::GPUMiner::setDefaultDevice(openclDevice); if (!ProofOfWork::GPUMiner::configureGPU(
ProofOfWork::GPUMiner::setNumInstances(miningThreads); m_openclPlatform,
ProofOfWork::GPUMiner::setDagChunks(dagChunks); m_openclDevice,
m_clAllowCPU,
m_extraGPUMemory,
m_forceSingleChunk,
m_currentBlock
))
{
cout << "No GPU device with sufficient memory was found. Can't GPU mine. Remove the -G argument" << endl;
exit(1);
}
} }
if (mode == OperationMode::DAGInit) if (mode == OperationMode::DAGInit)
doInitDAG(initDAG); doInitDAG(m_initDAG);
else if (mode == OperationMode::Benchmark) else if (mode == OperationMode::Benchmark)
doBenchmark(m_minerType, phoneHome, benchmarkWarmup, benchmarkTrial, benchmarkTrials); doBenchmark(m_minerType, m_phoneHome, m_benchmarkWarmup, m_benchmarkTrial, m_benchmarkTrials);
else if (mode == OperationMode::Farm) else if (mode == OperationMode::Farm)
doFarm(m_minerType, farmURL, farmRecheckPeriod); doFarm(m_minerType, m_farmURL, m_farmRecheckPeriod);
} }
static void streamHelp(ostream& _out) static void streamHelp(ostream& _out)
@ -311,7 +317,11 @@ 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 << " --allow-opencl-cpu Allows CPU to be considered as an OpenCL device if the OpenCL platform supports it." << endl
<< " --list-devices List the detected OpenCL devices and exit." <<endl
<< " --current-block Let the miner know the current block number at configuration time. Will help determine DAG size and required GPU memory." <<endl
<< " --cl-extragpu-mem Set the memory (in MB) you believe your GPU requires for stuff other than mining. Windows rendering e.t.c.." <<endl
<< " --force-single-chunk Force DAG uploading in a single chunk against OpenCL's judgement. Use at your own risk." <<endl
; ;
} }
@ -447,7 +457,7 @@ private:
cnote << "Grabbing DAG for" << newSeedHash; cnote << "Grabbing DAG for" << newSeedHash;
if (!(dag = EthashAux::full(newSeedHash, true, [&](unsigned _pc){ cout << "\rCreating DAG. " << _pc << "% done..." << flush; return 0; }))) if (!(dag = EthashAux::full(newSeedHash, true, [&](unsigned _pc){ cout << "\rCreating DAG. " << _pc << "% done..." << flush; return 0; })))
BOOST_THROW_EXCEPTION(DAGCreationFailure()); BOOST_THROW_EXCEPTION(DAGCreationFailure());
if (precompute) if (m_precompute)
EthashAux::computeFull(sha3(newSeedHash), true); EthashAux::computeFull(sha3(newSeedHash), true);
if (hh != current.headerHash) if (hh != current.headerHash)
{ {
@ -496,22 +506,27 @@ private:
/// Mining options /// Mining options
MinerType m_minerType = MinerType::CPU; MinerType m_minerType = MinerType::CPU;
unsigned openclPlatform = 0; unsigned m_openclPlatform = 0;
unsigned openclDevice = 0; unsigned m_openclDevice = 0;
unsigned miningThreads = UINT_MAX; unsigned m_miningThreads = UINT_MAX;
unsigned dagChunks = 1; bool m_shouldListDevices = false;
bool m_clAllowCPU = false;
bool m_forceSingleChunk = false;
boost::optional<uint64_t> m_currentBlock;
// default value is 350MB of GPU memory for other stuff (windows system rendering, e.t.c.)
unsigned m_extraGPUMemory = 350000000;
/// DAG initialisation param. /// DAG initialisation param.
unsigned initDAG = 0; unsigned m_initDAG = 0;
/// Benchmarking params /// Benchmarking params
bool phoneHome = true; bool m_phoneHome = true;
unsigned benchmarkWarmup = 3; unsigned m_benchmarkWarmup = 3;
unsigned benchmarkTrial = 3; unsigned m_benchmarkTrial = 3;
unsigned benchmarkTrials = 5; unsigned m_benchmarkTrials = 5;
/// Farm params /// Farm params
string farmURL = "http://127.0.0.1:8545"; string m_farmURL = "http://127.0.0.1:8545";
unsigned farmRecheckPeriod = 500; unsigned m_farmRecheckPeriod = 500;
bool precompute = true; bool m_precompute = true;
}; };

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;

12
libdevcore/Base64.cpp

@ -29,11 +29,13 @@
#include "Base64.h" #include "Base64.h"
using namespace dev; using namespace dev;
static inline bool is_base64(byte c) { static inline bool is_base64(byte c)
{
return (isalnum(c) || (c == '+') || (c == '/')); return (isalnum(c) || (c == '+') || (c == '/'));
} }
static inline byte find_base64_char_index(byte c) { static inline byte find_base64_char_index(byte c)
{
if ('A' <= c && c <= 'Z') return c - 'A'; if ('A' <= c && c <= 'Z') return c - 'A';
else if ('a' <= c && c <= 'z') return c - 'a' + 1 + find_base64_char_index('Z'); else if ('a' <= c && c <= 'z') return c - 'a' + 1 + find_base64_char_index('Z');
else if ('0' <= c && c <= '9') return c - '0' + 1 + find_base64_char_index('z'); else if ('0' <= c && c <= '9') return c - '0' + 1 + find_base64_char_index('z');
@ -42,7 +44,8 @@ static inline byte find_base64_char_index(byte c) {
else return 1 + find_base64_char_index('/'); else return 1 + find_base64_char_index('/');
} }
std::string dev::toBase64(bytesConstRef _in) { std::string dev::toBase64(bytesConstRef _in)
{
static const char base64_chars[] = static const char base64_chars[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz" "abcdefghijklmnopqrstuvwxyz"
@ -91,7 +94,8 @@ std::string dev::toBase64(bytesConstRef _in) {
return ret; return ret;
} }
bytes dev::fromBase64(std::string const& encoded_string) { bytes dev::fromBase64(std::string const& encoded_string)
{
auto in_len = encoded_string.size(); auto in_len = encoded_string.size();
int i = 0; int i = 0;
int j = 0; int j = 0;

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.26";
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

4
libdevcore/Exceptions.h

@ -41,11 +41,11 @@ private:
std::string m_message; std::string m_message;
}; };
#define DEV_SIMPLE_EXCEPTION(X) struct X: virtual Exception { public: X(): Exception(#X) {} } #define DEV_SIMPLE_EXCEPTION(X) struct X: virtual Exception { const char* what() const noexcept override { return #X; } }
/// Base class for all RLP exceptions. /// Base class for all RLP exceptions.
struct RLPException: virtual Exception { RLPException(std::string _message = std::string()): Exception(_message) {} }; struct RLPException: virtual Exception { RLPException(std::string _message = std::string()): Exception(_message) {} };
#define DEV_SIMPLE_EXCEPTION_RLP(X) struct X: virtual RLPException { public: X(): RLPException(#X) {} } #define DEV_SIMPLE_EXCEPTION_RLP(X) struct X: virtual RLPException { const char* what() const noexcept override { return #X; } }
DEV_SIMPLE_EXCEPTION_RLP(BadCast); DEV_SIMPLE_EXCEPTION_RLP(BadCast);
DEV_SIMPLE_EXCEPTION_RLP(BadRLP); DEV_SIMPLE_EXCEPTION_RLP(BadRLP);

2
libdevcore/FixedHash.h

@ -100,7 +100,7 @@ public:
FixedHash operator|(FixedHash const& _c) const { return FixedHash(*this) |= _c; } FixedHash operator|(FixedHash const& _c) const { return FixedHash(*this) |= _c; }
FixedHash& operator&=(FixedHash const& _c) { for (unsigned i = 0; i < N; ++i) m_data[i] &= _c.m_data[i]; return *this; } FixedHash& operator&=(FixedHash const& _c) { for (unsigned i = 0; i < N; ++i) m_data[i] &= _c.m_data[i]; return *this; }
FixedHash operator&(FixedHash const& _c) const { return FixedHash(*this) &= _c; } FixedHash operator&(FixedHash const& _c) const { return FixedHash(*this) &= _c; }
FixedHash& operator~() { for (unsigned i = 0; i < N; ++i) m_data[i] = ~m_data[i]; return *this; } FixedHash operator~() const { FixedHash ret; for (unsigned i = 0; i < N; ++i) ret[i] = ~m_data[i]; return ret; }
/// @returns true if all bytes in @a _c are set in this object. /// @returns true if all bytes in @a _c are set in this object.
bool contains(FixedHash const& _c) const { return (*this & _c) == _c; } bool contains(FixedHash const& _c) const { return (*this & _c) == _c; }

2
libdevcore/Log.cpp

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

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;

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; }

2
libdevcrypto/CryptoPP.cpp

@ -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);

6
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();
@ -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();
} }

227
libethash-cl/ethash_cl_miner.cpp

@ -32,11 +32,11 @@
#include <vector> #include <vector>
#include <libethash/util.h> #include <libethash/util.h>
#include <libethash/ethash.h> #include <libethash/ethash.h>
#include <libethash/internal.h>
#include "ethash_cl_miner.h" #include "ethash_cl_miner.h"
#include "ethash_cl_miner_kernel.h" #include "ethash_cl_miner_kernel.h"
#define ETHASH_BYTES 32 #define ETHASH_BYTES 32
#define ETHASH_CL_MINIMUM_MEMORY 2000000000
// workaround lame platforms // workaround lame platforms
#if !CL_VERSION_1_2 #if !CL_VERSION_1_2
@ -51,12 +51,14 @@ 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
// Types of OpenCL devices we are interested in
#define ETHCL_QUERIED_DEVICE_TYPES (CL_DEVICE_TYPE_GPU | CL_DEVICE_TYPE_ACCELERATOR)
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 +73,54 @@ 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; unsigned platform_num = min<unsigned>(_platformId, platforms.size() - 1);
unsigned platform_num = std::min<unsigned>(_platformId, platforms.size() - 1); vector<cl::Device> devices = getDevices(platforms, _platformId);
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() std::vector<cl::Device> ethash_cl_miner::getDevices(std::vector<cl::Platform> const& _platforms, unsigned _platformId)
{ {
std::vector<cl::Platform> platforms; vector<cl::Device> devices;
unsigned platform_num = min<unsigned>(_platformId, _platforms.size() - 1);
_platforms[platform_num].getDevices(
s_allowCPU ? CL_DEVICE_TYPE_ALL : ETHCL_QUERIED_DEVICE_TYPES,
&devices
);
return devices;
}
unsigned ethash_cl_miner::getNumPlatforms()
{
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,9 +128,7 @@ unsigned ethash_cl_miner::get_num_devices(unsigned _platformId)
return 0; return 0;
} }
std::vector<cl::Device> devices; vector<cl::Device> devices = getDevices(platforms, _platformId);
unsigned platform_num = std::min<unsigned>(_platformId, platforms.size() - 1);
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.");
@ -127,9 +137,49 @@ unsigned ethash_cl_miner::get_num_devices(unsigned _platformId)
return devices.size(); return devices.size();
} }
bool ethash_cl_miner::haveSufficientGPUMemory() bool ethash_cl_miner::configureGPU(
bool _allowCPU,
unsigned _extraGPUMemory,
bool _forceSingleChunk,
boost::optional<uint64_t> _currentBlock
)
{ {
std::vector<cl::Platform> platforms; s_allowCPU = _allowCPU;
s_forceSingleChunk = _forceSingleChunk;
s_extraRequiredGPUMem = _extraGPUMemory;
// by default let's only consider the DAG of the first epoch
uint64_t dagSize = _currentBlock ? ethash_get_datasize(*_currentBlock) : 1073739904U;
uint64_t requiredSize = dagSize + _extraGPUMemory;
return searchForAllDevices([&requiredSize](cl::Device const _device) -> bool
{
cl_ulong result;
_device.getInfo(CL_DEVICE_GLOBAL_MEM_SIZE, &result);
if (result >= requiredSize)
{
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 < " << requiredSize << " bytes of memory required"
);
return false;
}
);
}
bool ethash_cl_miner::s_allowCPU = false;
bool ethash_cl_miner::s_forceSingleChunk = false;
unsigned ethash_cl_miner::s_extraRequiredGPUMem;
bool ethash_cl_miner::searchForAllDevices(function<bool(cl::Device const&)> _callback)
{
vector<cl::Platform> platforms;
cl::Platform::get(&platforms); cl::Platform::get(&platforms);
if (platforms.empty()) if (platforms.empty())
{ {
@ -137,50 +187,30 @@ bool ethash_cl_miner::haveSufficientGPUMemory()
return false; return false;
} }
for (unsigned i = 0; i < platforms.size(); ++i) for (unsigned i = 0; i < platforms.size(); ++i)
if (haveSufficientGPUMemory(i)) if (searchForAllDevices(i, _callback))
return true; return true;
return false; return false;
} }
bool ethash_cl_miner::haveSufficientGPUMemory(unsigned _platformId) bool ethash_cl_miner::searchForAllDevices(unsigned _platformId, function<bool(cl::Device const&)> _callback)
{ {
std::vector<cl::Platform> platforms; vector<cl::Platform> platforms;
cl::Platform::get(&platforms); cl::Platform::get(&platforms);
if (_platformId >= platforms.size()) if (_platformId >= platforms.size())
return false; return false;
std::vector<cl::Device> devices; vector<cl::Device> devices = getDevices(platforms, _platformId);
unsigned platform_num = std::min<unsigned>(_platformId, platforms.size() - 1);
platforms[platform_num].getDevices(CL_DEVICE_TYPE_ALL, &devices);
if (devices.empty())
return false;
for (cl::Device const& device: devices) for (cl::Device const& device: devices)
{ if (_callback(device))
cl_ulong result;
device.getInfo(CL_DEVICE_GLOBAL_MEM_SIZE, &result);
if (result >= ETHASH_CL_MINIMUM_MEMORY)
{
ETHCL_LOG(
"Found suitable OpenCL device [" << device.getInfo<CL_DEVICE_NAME>()
<< "] with " << result << " bytes of GPU memory"
);
return true; return true;
}
else
ETHCL_LOG(
"OpenCL device " << device.getInfo<CL_DEVICE_NAME>()
<< " has insufficient GPU memory." << result <<
" bytes of memory found < " << ETHASH_CL_MINIMUM_MEMORY << " bytes of memory required"
);
}
return false; return false;
} }
void ethash_cl_miner::listDevices() void ethash_cl_miner::doForAllDevices(function<void(cl::Device const&)> _callback)
{ {
std::vector<cl::Platform> platforms; vector<cl::Platform> platforms;
cl::Platform::get(&platforms); cl::Platform::get(&platforms);
if (platforms.empty()) if (platforms.empty())
{ {
@ -188,26 +218,31 @@ void ethash_cl_miner::listDevices()
return; return;
} }
for (unsigned i = 0; i < platforms.size(); ++i) for (unsigned i = 0; i < platforms.size(); ++i)
listDevices(i); doForAllDevices(i, _callback);
} }
void ethash_cl_miner::listDevices(unsigned _platformId) void ethash_cl_miner::doForAllDevices(unsigned _platformId, function<void(cl::Device const&)> _callback)
{ {
std::vector<cl::Platform> platforms; vector<cl::Platform> platforms;
cl::Platform::get(&platforms); cl::Platform::get(&platforms);
if (_platformId >= platforms.size()) if (_platformId >= platforms.size())
return; return;
std::string outString ="Listing OpenCL devices for platform " + to_string(_platformId) + "\n[deviceID] deviceName\n"; vector<cl::Device> devices = getDevices(platforms, _platformId);
std::vector<cl::Device> devices;
platforms[_platformId].getDevices(CL_DEVICE_TYPE_ALL, &devices);
unsigned i = 0;
std::string deviceString;
for (cl::Device const& device: devices) for (cl::Device const& device: devices)
{ _callback(device);
outString += "[" + to_string(i) + "] " + device.getInfo<CL_DEVICE_NAME>() + "\n"; }
++i;
} void ethash_cl_miner::listDevices()
{
string outString ="\nListing OpenCL devices.\nFORMAT: [deviceID] deviceName\n";
unsigned int i = 0;
doForAllDevices([&outString, &i](cl::Device const _device)
{
outString += "[" + to_string(i) + "] " + _device.getInfo<CL_DEVICE_NAME>() + "\n";
++i;
}
);
ETHCL_LOG(outString); ETHCL_LOG(outString);
} }
@ -222,19 +257,13 @@ bool ethash_cl_miner::init(
uint64_t _dagSize, uint64_t _dagSize,
unsigned workgroup_size, unsigned workgroup_size,
unsigned _platformId, unsigned _platformId,
unsigned _deviceId, unsigned _deviceId
unsigned _dagChunksNum
) )
{ {
// for now due to the .cl kernels we can only have either 1 big chunk or 4 chunks
assert(_dagChunksNum == 1 || _dagChunksNum == 4);
// now create the number of chunk buffers
m_dagChunksNum = _dagChunksNum;
// get all platforms // get all platforms
try try
{ {
std::vector<cl::Platform> platforms; vector<cl::Platform> platforms;
cl::Platform::get(&platforms); cl::Platform::get(&platforms);
if (platforms.empty()) if (platforms.empty())
{ {
@ -243,12 +272,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 = getDevices(platforms, _platformId);
platforms[_platformId].getDevices(CL_DEVICE_TYPE_ALL, &devices);
if (devices.empty()) if (devices.empty())
{ {
ETHCL_LOG("No OpenCL devices found."); ETHCL_LOG("No OpenCL devices found.");
@ -256,10 +284,27 @@ 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 (s_forceSingleChunk || result >= _dagSize)
{
m_dagChunksNum = 1;
ETHCL_LOG(
((result <= _dagSize && s_forceSingleChunk) ? "Forcing single chunk. Good luck!\n" : "") <<
"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.");
@ -269,7 +314,7 @@ bool ethash_cl_miner::init(
m_opencl_1_1 = true; m_opencl_1_1 = true;
// create context // create context
m_context = cl::Context(std::vector<cl::Device>(&device, &device + 1)); m_context = cl::Context(vector<cl::Device>(&device, &device + 1));
m_queue = cl::CommandQueue(m_context, device); m_queue = cl::CommandQueue(m_context, device);
// use requested workgroup size, but we require multiple of 8 // use requested workgroup size, but we require multiple of 8
@ -278,11 +323,11 @@ bool ethash_cl_miner::init(
// patch source code // patch source code
// note: ETHASH_CL_MINER_KERNEL is simply ethash_cl_miner_kernel.cl compiled // note: ETHASH_CL_MINER_KERNEL is simply ethash_cl_miner_kernel.cl compiled
// into a byte array by bin2h.cmake. There is no need to load the file by hand in runtime // into a byte array by bin2h.cmake. There is no need to load the file by hand in runtime
std::string code(ETHASH_CL_MINER_KERNEL, ETHASH_CL_MINER_KERNEL + ETHASH_CL_MINER_KERNEL_SIZE); string code(ETHASH_CL_MINER_KERNEL, ETHASH_CL_MINER_KERNEL + ETHASH_CL_MINER_KERNEL_SIZE);
add_definition(code, "GROUP_SIZE", m_workgroup_size); addDefinition(code, "GROUP_SIZE", m_workgroup_size);
add_definition(code, "DAG_SIZE", (unsigned)(_dagSize / ETHASH_MIX_BYTES)); addDefinition(code, "DAG_SIZE", (unsigned)(_dagSize / ETHASH_MIX_BYTES));
add_definition(code, "ACCESSES", ETHASH_ACCESSES); addDefinition(code, "ACCESSES", ETHASH_ACCESSES);
add_definition(code, "MAX_OUTPUTS", c_max_search_results); addDefinition(code, "MAX_OUTPUTS", c_max_search_results);
//debugf("%s", code.c_str()); //debugf("%s", code.c_str());
// create miner OpenCL program // create miner OpenCL program
@ -301,7 +346,7 @@ bool ethash_cl_miner::init(
ETHCL_LOG(program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(device).c_str()); ETHCL_LOG(program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(device).c_str());
return false; return false;
} }
if (_dagChunksNum == 1) if (m_dagChunksNum == 1)
{ {
ETHCL_LOG("Loading single big chunk kernels"); ETHCL_LOG("Loading single big chunk kernels");
m_hash_kernel = cl::Kernel(program, "ethash_hash"); m_hash_kernel = cl::Kernel(program, "ethash_hash");
@ -315,13 +360,13 @@ bool ethash_cl_miner::init(
} }
// create buffer for dag // create buffer for dag
if (_dagChunksNum == 1) if (m_dagChunksNum == 1)
{ {
ETHCL_LOG("Creating one big buffer"); ETHCL_LOG("Creating one big buffer");
m_dagChunks.push_back(cl::Buffer(m_context, CL_MEM_READ_ONLY, _dagSize)); m_dagChunks.push_back(cl::Buffer(m_context, CL_MEM_READ_ONLY, _dagSize));
} }
else else
for (unsigned i = 0; i < _dagChunksNum; i++) for (unsigned i = 0; i < m_dagChunksNum; i++)
{ {
// TODO Note: If we ever change to _dagChunksNum other than 4, then the size would need recalculation // TODO Note: If we ever change to _dagChunksNum other than 4, then the size would need recalculation
ETHCL_LOG("Creating buffer for chunk " << i); ETHCL_LOG("Creating buffer for chunk " << i);
@ -336,7 +381,7 @@ bool ethash_cl_miner::init(
ETHCL_LOG("Creating buffer for header."); ETHCL_LOG("Creating buffer for header.");
m_header = cl::Buffer(m_context, CL_MEM_READ_ONLY, 32); m_header = cl::Buffer(m_context, CL_MEM_READ_ONLY, 32);
if (_dagChunksNum == 1) if (m_dagChunksNum == 1)
{ {
ETHCL_LOG("Mapping one big chunk."); ETHCL_LOG("Mapping one big chunk.");
m_queue.enqueueWriteBuffer(m_dagChunks[0], CL_TRUE, 0, _dagSize, _dag); m_queue.enqueueWriteBuffer(m_dagChunks[0], CL_TRUE, 0, _dagSize, _dag);
@ -345,12 +390,12 @@ bool ethash_cl_miner::init(
{ {
// TODO Note: If we ever change to _dagChunksNum other than 4, then the size would need recalculation // TODO Note: If we ever change to _dagChunksNum other than 4, then the size would need recalculation
void* dag_ptr[4]; void* dag_ptr[4];
for (unsigned i = 0; i < _dagChunksNum; i++) for (unsigned i = 0; i < m_dagChunksNum; i++)
{ {
ETHCL_LOG("Mapping chunk " << i); ETHCL_LOG("Mapping chunk " << i);
dag_ptr[i] = m_queue.enqueueMapBuffer(m_dagChunks[i], true, m_opencl_1_1 ? CL_MAP_WRITE : CL_MAP_WRITE_INVALIDATE_REGION, 0, (i == 3) ? (_dagSize - 3 * ((_dagSize >> 9) << 7)) : (_dagSize >> 9) << 7); dag_ptr[i] = m_queue.enqueueMapBuffer(m_dagChunks[i], true, m_opencl_1_1 ? CL_MAP_WRITE : CL_MAP_WRITE_INVALIDATE_REGION, 0, (i == 3) ? (_dagSize - 3 * ((_dagSize >> 9) << 7)) : (_dagSize >> 9) << 7);
} }
for (unsigned i = 0; i < _dagChunksNum; i++) for (unsigned i = 0; i < m_dagChunksNum; i++)
{ {
memcpy(dag_ptr[i], (char *)_dag + i*((_dagSize >> 9) << 7), (i == 3) ? (_dagSize - 3 * ((_dagSize >> 9) << 7)) : (_dagSize >> 9) << 7); memcpy(dag_ptr[i], (char *)_dag + i*((_dagSize >> 9) << 7), (i == 3) ? (_dagSize - 3 * ((_dagSize >> 9) << 7)) : (_dagSize >> 9) << 7);
m_queue.enqueueUnmapMemObject(m_dagChunks[i], dag_ptr[i]); m_queue.enqueueUnmapMemObject(m_dagChunks[i], dag_ptr[i]);
@ -382,7 +427,7 @@ void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook
uint64_t start_nonce; uint64_t start_nonce;
unsigned buf; unsigned buf;
}; };
std::queue<pending_batch> pending; queue<pending_batch> pending;
static uint32_t const c_zero = 0; static uint32_t const c_zero = 0;
@ -408,8 +453,8 @@ void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook
m_search_kernel.setArg(argPos + 2, ~0u); m_search_kernel.setArg(argPos + 2, ~0u);
unsigned buf = 0; unsigned buf = 0;
std::random_device engine; random_device engine;
uint64_t start_nonce = std::uniform_int_distribution<uint64_t>()(engine); uint64_t start_nonce = uniform_int_distribution<uint64_t>()(engine);
for (;; start_nonce += c_search_batch_size) for (;; start_nonce += c_search_batch_size)
{ {
// supply output buffer to kernel // supply output buffer to kernel
@ -432,7 +477,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)

34
libethash-cl/ethash_cl_miner.h

@ -12,6 +12,7 @@
#include "cl.hpp" #include "cl.hpp"
#endif #endif
#include <boost/optional.hpp>
#include <time.h> #include <time.h>
#include <functional> #include <functional>
#include <libethash/ethash.h> #include <libethash/ethash.h>
@ -32,21 +33,27 @@ public:
ethash_cl_miner(); ethash_cl_miner();
~ethash_cl_miner(); ~ethash_cl_miner();
static unsigned get_num_platforms(); static bool searchForAllDevices(unsigned _platformId, std::function<bool(cl::Device const&)> _callback);
static unsigned get_num_devices(unsigned _platformId = 0); static bool searchForAllDevices(std::function<bool(cl::Device const&)> _callback);
static void doForAllDevices(unsigned _platformId, std::function<void(cl::Device const&)> _callback);
static void doForAllDevices(std::function<void(cl::Device const&)> _callback);
static unsigned getNumPlatforms();
static unsigned getNumDevices(unsigned _platformId = 0);
static std::string platform_info(unsigned _platformId = 0, unsigned _deviceId = 0); static std::string platform_info(unsigned _platformId = 0, unsigned _deviceId = 0);
static bool haveSufficientGPUMemory();
static bool haveSufficientGPUMemory(unsigned _platformId);
static void listDevices(); static void listDevices();
static void listDevices(unsigned _platformId); static bool configureGPU(
bool _allowCPU,
unsigned _extraGPUMemory,
bool _forceSingleChunk,
boost::optional<uint64_t> _currentBlock
);
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);
@ -55,17 +62,28 @@ public:
void search_chunk(uint8_t const* header, uint64_t target, search_hook& hook); void search_chunk(uint8_t const* header, uint64_t target, search_hook& hook);
private: private:
static std::vector<cl::Device> getDevices(std::vector<cl::Platform> const& _platforms, unsigned _platformId);
enum { c_max_search_results = 63, c_num_buffers = 2, c_hash_batch_size = 1024, c_search_batch_size = 1024*256 }; enum { c_max_search_results = 63, c_num_buffers = 2, c_hash_batch_size = 1024, c_search_batch_size = 1024*256 };
cl::Context m_context; cl::Context m_context;
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;
/// Force dag upload to GPU in a single chunk even if OpenCL thinks you can't do it. Use at your own risk.
static bool s_forceSingleChunk;
/// Allow CPU to appear as an OpenCL device or not. Default is false
static bool s_allowCPU;
/// GPU memory required for other things, like window rendering e.t.c.
/// User can set it via the --cl-extragpu-mem argument.
static unsigned s_extraRequiredGPUMem;
}; };

2
libethash-cl/ethash_cl_miner_kernel.cl

@ -585,4 +585,4 @@ __kernel void ethash_search_chunks(
uint slot = min(convert_uint(MAX_OUTPUTS), convert_uint(atomic_inc(&g_output[0]) + 1)); uint slot = min(convert_uint(MAX_OUTPUTS), convert_uint(atomic_inc(&g_output[0]) + 1));
g_output[slot] = gid; g_output[slot] = gid;
} }
} }

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);;
} }
} }

31
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
{ {
@ -119,28 +123,43 @@ struct ImportRequirements
class Signal class Signal
{ {
public: public:
using Callback = std::function<void()>;
class HandlerAux class HandlerAux
{ {
friend class Signal; friend class Signal;
public: public:
~HandlerAux() { if (m_s) m_s->m_fire.erase(m_i); m_s = nullptr; } ~HandlerAux() { if (m_s) m_s->m_fire.erase(m_i); m_s = nullptr; }
void reset() { m_s = nullptr; }
void fire() { m_h(); }
private: private:
HandlerAux(unsigned _i, Signal* _s): m_i(_i), m_s(_s) {} HandlerAux(unsigned _i, Signal* _s, Callback const& _h): m_i(_i), m_s(_s), m_h(_h) {}
unsigned m_i = 0; unsigned m_i = 0;
Signal* m_s = nullptr; Signal* m_s = nullptr;
Callback m_h;
}; };
using Callback = std::function<void()>; ~Signal()
{
for (auto const& h : m_fire)
h.second->reset();
}
std::shared_ptr<HandlerAux> add(Callback const& _h) { auto n = m_fire.empty() ? 0 : (m_fire.rbegin()->first + 1); m_fire[n] = _h; return std::shared_ptr<HandlerAux>(new HandlerAux(n, this)); } std::shared_ptr<HandlerAux> add(Callback const& _h)
{
auto n = m_fire.empty() ? 0 : (m_fire.rbegin()->first + 1);
auto h = std::shared_ptr<HandlerAux>(new HandlerAux(n, this, _h));
m_fire[n] = h;
return h;
}
void operator()() { for (auto const& f: m_fire) f.second(); } void operator()() { for (auto const& f: m_fire) f.second->fire(); }
private: private:
std::map<unsigned, Callback> m_fire; std::map<unsigned, std::shared_ptr<Signal::HandlerAux>> m_fire;
}; };
using Handler = std::shared_ptr<Signal::HandlerAux>; using Handler = std::shared_ptr<Signal::HandlerAux>;
@ -156,8 +175,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); }

23
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() << ")";
} }
} }
@ -371,7 +373,7 @@ std::string Ethash::GPUMiner::platformInfo()
unsigned Ethash::GPUMiner::getNumDevices() unsigned Ethash::GPUMiner::getNumDevices()
{ {
return ethash_cl_miner::get_num_devices(s_platformId); return ethash_cl_miner::getNumDevices(s_platformId);
} }
void Ethash::GPUMiner::listDevices() void Ethash::GPUMiner::listDevices()
@ -379,9 +381,18 @@ void Ethash::GPUMiner::listDevices()
return ethash_cl_miner::listDevices(); return ethash_cl_miner::listDevices();
} }
bool Ethash::GPUMiner::haveSufficientMemory() bool Ethash::GPUMiner::configureGPU(
unsigned _platformId,
unsigned _deviceId,
bool _allowCPU,
unsigned _extraGPUMemory,
bool _forceSingleChunk,
boost::optional<uint64_t> _currentBlock
)
{ {
return ethash_cl_miner::haveSufficientGPUMemory(); s_platformId = _platformId;
s_deviceId = _deviceId;
return ethash_cl_miner::configureGPU(_allowCPU, _extraGPUMemory, _forceSingleChunk, _currentBlock);
} }
#endif #endif

18
libethcore/Ethash.h

@ -87,11 +87,8 @@ 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 void setDefaultPlatform(unsigned) {}
static void setDagChunks(unsigned) {}
static void setDefaultDevice(unsigned) {}
static void listDevices() {} static void listDevices() {}
static bool haveSufficientMemory() { return false; } static bool configureGPU(unsigned, unsigned, bool, unsigned, bool, boost::optional<uint64_t>) { return false; }
static void setNumInstances(unsigned _instances) { s_numInstances = std::min<unsigned>(_instances, std::thread::hardware_concurrency()); } static void setNumInstances(unsigned _instances) { s_numInstances = std::min<unsigned>(_instances, std::thread::hardware_concurrency()); }
protected: protected:
void kickOff() override void kickOff() override
@ -120,11 +117,15 @@ public:
static std::string platformInfo(); static std::string platformInfo();
static unsigned getNumDevices(); static unsigned getNumDevices();
static void listDevices(); static void listDevices();
static bool haveSufficientMemory(); static bool configureGPU(
static void setDefaultPlatform(unsigned _id) { s_platformId = _id; } unsigned _platformId,
static void setDefaultDevice(unsigned _id) { s_deviceId = _id; } unsigned _deviceId,
bool _allowCPU,
unsigned _extraGPUMemory,
bool _forceSingleChunk,
boost::optional<uint64_t> _currentBlock
);
static void setNumInstances(unsigned _instances) { s_numInstances = std::min<unsigned>(_instances, getNumDevices()); } static void setNumInstances(unsigned _instances) { s_numInstances = std::min<unsigned>(_instances, getNumDevices()); }
static void setDagChunks(unsigned _dagChunks) { s_dagChunks = _dagChunks; }
protected: protected:
void kickOff() override; void kickOff() override;
@ -143,7 +144,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;

5
libethcore/EthashAux.cpp

@ -54,6 +54,11 @@ uint64_t EthashAux::cacheSize(BlockInfo const& _header)
return ethash_get_cachesize((uint64_t)_header.number); return ethash_get_cachesize((uint64_t)_header.number);
} }
uint64_t EthashAux::dataSize(uint64_t _blockNumber)
{
return ethash_get_datasize(_blockNumber);
}
h256 EthashAux::seedHash(unsigned _number) h256 EthashAux::seedHash(unsigned _number)
{ {
unsigned epoch = _number / ETHASH_EPOCH_LENGTH; unsigned epoch = _number / ETHASH_EPOCH_LENGTH;

1
libethcore/EthashAux.h

@ -66,6 +66,7 @@ public:
static h256 seedHash(unsigned _number); static h256 seedHash(unsigned _number);
static uint64_t number(h256 const& _seedHash); static uint64_t number(h256 const& _seedHash);
static uint64_t cacheSize(BlockInfo const& _header); static uint64_t cacheSize(BlockInfo const& _header);
static uint64_t dataSize(uint64_t _blockNumber);
static LightType light(h256 const& _seedHash); static LightType light(h256 const& _seedHash);

251
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,47 +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 (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.first.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); try
if (m_onBad) {
m_onBad(ex); // Nonce & uncle nonces already verified in verification thread at this point.
// NOTE: don't reimport since the queue should guarantee everything in the right order. ImportRoute r;
// Can't continue - chain bad. DEV_TIMED_ABOVE(Block import, 500)
badBlocks.push_back(block.first.hash()); 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));
} }
@ -355,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, _ir), _stateDB, _ir));
} }
catch (UnknownParent&) catch (UnknownParent&)
{ {
@ -380,29 +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& ex) catch (Exception& ex)
{ {
clog(BlockChainNote) << " Malformed block: " << diagnostic_information(ex); // clog(BlockChainNote) << " Malformed block: " << diagnostic_information(ex);
ex << errinfo_now(time(0)); ex << errinfo_now(time(0));
ex << errinfo_block(_block); 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.
@ -417,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";
@ -446,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();
@ -473,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;
@ -503,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();
@ -533,23 +537,18 @@ ImportRoute BlockChain::import(BlockInfo const& _bi, bytes const& _block, Overla
} }
catch (Exception& ex) catch (Exception& ex)
{ {
clog(BlockChainWarn) << " Malformed block: " << diagnostic_information(ex);
clog(BlockChainWarn) << "Block: " << _bi.hash();
clog(BlockChainWarn) << _bi;
clog(BlockChainWarn) << "Block parent: " << _bi.parentHash;
clog(BlockChainWarn) << BlockInfo(block(_bi.parentHash));
ex << errinfo_now(time(0)); ex << errinfo_now(time(0));
ex << errinfo_block(_block); ex << errinfo_block(_block.block.toBytes());
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.";
@ -562,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)
@ -575,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));
@ -603,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)
@ -619,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
@ -640,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)
@ -673,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())
@ -921,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)); }
@ -1060,3 +1065,63 @@ 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, ImportRequirements::value _ir)
{
VerifiedBlockRef res;
try
{
Strictness strictness = Strictness::CheckEverything;
if (_ir & ~ImportRequirements::ValidNonce)
strictness = Strictness::IgnoreNonce;
res.info.populate(_block, strictness);
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);
}

7
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,9 @@ 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&)>(), ImportRequirements::value _ir = ImportRequirements::Default);
/// Change the function that is called with a bad block. /// Change the function that is called with a bad block.
template <class T> void setOnBad(T const& _t) { m_onBad = _t; } template <class T> void setOnBad(T const& _t) { m_onBad = _t; }

257
libethereum/BlockQueue.cpp

@ -22,10 +22,10 @@
#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" #include "State.h"
using namespace std; using namespace std;
using namespace dev; using namespace dev;
@ -37,8 +37,16 @@ const char* BlockQueueChannel::name() { return EthOrange "[]>"; }
const char* BlockQueueChannel::name() { return EthOrange "▣┅▶"; } const char* BlockQueueChannel::name() { return EthOrange "▣┅▶"; }
#endif #endif
size_t const c_maxKnownCount = 100000;
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;
@ -57,11 +65,29 @@ BlockQueue::~BlockQueue()
i.join(); i.join();
} }
void BlockQueue::clear()
{
WriteGuard l(m_lock);
DEV_INVARIANT_CHECK;
Guard l2(m_verification);
m_readySet.clear();
m_drainingSet.clear();
m_verified.clear();
m_unverified.clear();
m_unknownSet.clear();
m_unknown.clear();
m_future.clear();
m_unknownSize = 0;
m_unknownCount = 0;
m_knownSize = 0;
m_knownCount = 0;
}
void BlockQueue::verifierBody() void BlockQueue::verifierBody()
{ {
while (!m_deleting) while (!m_deleting)
{ {
std::pair<h256, bytes> work; UnverifiedBlock work;
{ {
unique_lock<Mutex> l(m_verification); unique_lock<Mutex> l(m_verification);
@ -71,51 +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.verified = BlockChain::verifyBlock(res.blockData, m_onBad);
{
res.first.populate(res.second, CheckEverything, work.first);
res.first.verifyInternals(&res.second);
}
catch (Exception& ex)
{
clog(BlockChainNote) << " Malformed block: " << diagnostic_information(ex);
badBlock(res.second, ex.what());
ex << errinfo_now(time(0));
ex << errinfo_block(res.second);
if (m_onBad)
m_onBad(ex);
throw;
}
RLP r(&res.second);
unsigned ii = 0;
for (auto const& uncle: r[2])
{
try
{
BlockInfo().populateFromHeader(RLP(uncle.data()), CheckEverything);
}
catch (Exception& ex)
{
clog(BlockChainNote) << " Malformed block header: " << diagnostic_information(ex);
badBlockHeader(uncle.data(), ex.what());
ex << errinfo_uncleIndex(ii);
ex << errinfo_now(time(0));
ex << errinfo_block(res.second);
if (m_onBad)
m_onBad(ex);
throw;
}
++ii;
}
} }
catch (...) catch (...)
{ {
@ -124,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_verified.push_back(move(m_verifying.front())); 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())
{
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;
@ -158,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:;
} }
} }
@ -223,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
@ -231,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;
} }
@ -240,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;
} }
@ -248,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);
@ -259,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));
} }
} }
m_knownBad += _bad; DEV_INVARIANT_CHECK;
return !m_readySet.empty(); }
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);
}
}
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)
@ -306,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);
} }
} }
@ -337,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()));
@ -353,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
@ -378,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);
@ -389,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()
@ -398,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;

36
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,27 +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; } 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.
@ -127,17 +142,22 @@ private:
h256Hash m_knownBad; ///< Set of blocks that we know will never be valid. h256Hash m_knownBad; ///< Set of blocks that we know will never be valid.
std::multimap<unsigned, std::pair<h256, bytes>> m_future; ///< Set of blocks that are not yet valid. Ordered by timestamp std::multimap<unsigned, std::pair<h256, bytes>> m_future; ///< Set of blocks that are not yet valid. Ordered by timestamp
Signal m_onReady; ///< Called when a subsequent call to import blocks will return a non-empty container. Be nice and exit fast. Signal m_onReady; ///< Called when a subsequent call to import blocks will return a non-empty container. Be nice and exit fast.
Signal m_onRoomAvailable; ///< Called when space for new blocks becomes availabe after a drain. Be nice and exit fast.
mutable Mutex m_verification; ///< Mutex that allows writing to m_verified, m_verifying and m_unverified. mutable Mutex m_verification; ///< Mutex that allows writing to m_verified, m_verifying and m_unverified.
std::condition_variable m_moreToVerify; ///< Signaled when m_unverified has a new entry. std::condition_variable m_moreToVerify; ///< Signaled when m_unverified has a new entry.
std::vector<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::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);

4
libethereum/CMakeLists.txt

@ -36,6 +36,10 @@ target_link_libraries(${EXECUTABLE} ${Boost_REGEX_LIBRARIES})
target_link_libraries(${EXECUTABLE} secp256k1) target_link_libraries(${EXECUTABLE} secp256k1)
if (JSONRPC) if (JSONRPC)
target_link_libraries(${EXECUTABLE} ${JSON_RPC_CPP_CLIENT_LIBRARIES}) 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() endif()
if (CMAKE_COMPILER_IS_MINGW) if (CMAKE_COMPILER_IS_MINGW)

323
libethereum/Client.cpp

@ -87,87 +87,100 @@ void VersionChecker::setOk()
} }
} }
void Client::onBadBlock(Exception& _ex) void Client::onBadBlock(Exception& _ex) const
{ {
// BAD BLOCK!!! // BAD BLOCK!!!
bytes const& block = *boost::get_error_info<errinfo_block>(_ex); bytes const* block = boost::get_error_info<errinfo_block>(_ex);
if (!&block) if (!block)
{ {
cwarn << "ODD: onBadBlock called but exception has no block in it."; cwarn << "ODD: onBadBlock called but exception has no block in it.";
return; return;
} }
badBlock(block, _ex.what()); badBlock(*block, _ex.what());
cwarn << boost::diagnostic_information(_ex, true);
#if ETH_JSONRPC || !ETH_TRUE #if ETH_JSONRPC || !ETH_TRUE
if (!m_sentinel.empty()) Json::Value report;
{
Json::Value report;
report["client"] = "cpp"; report["client"] = "cpp";
report["version"] = Version; report["version"] = Version;
report["protocolVersion"] = c_protocolVersion; report["protocolVersion"] = c_protocolVersion;
report["databaseVersion"] = c_databaseVersion; report["databaseVersion"] = c_databaseVersion;
report["errortype"] = _ex.what(); report["errortype"] = _ex.what();
report["block"] = toHex(block); report["block"] = toHex(*block);
report["hint"] = Json::Value(Json::objectValue);
// add the various hints. // add the various hints.
if (unsigned const* uncleIndex = boost::get_error_info<errinfo_uncleIndex>(_ex)) if (unsigned const* uncleIndex = boost::get_error_info<errinfo_uncleIndex>(_ex))
{ {
// uncle that failed. // uncle that failed.
report["hints"]["uncleIndex"] = *uncleIndex; report["hints"]["uncleIndex"] = *uncleIndex;
} }
else if (unsigned const* txIndex = boost::get_error_info<errinfo_transactionIndex>(_ex)) else if (unsigned const* txIndex = boost::get_error_info<errinfo_transactionIndex>(_ex))
{ {
// transaction that failed. // transaction that failed.
report["hints"]["transactionIndex"] = *txIndex; report["hints"]["transactionIndex"] = *txIndex;
} }
else else
{ {
// general block failure. // general block failure.
} }
if (string const* vmtraceJson = boost::get_error_info<errinfo_vmtrace>(_ex)) if (string const* vmtraceJson = boost::get_error_info<errinfo_vmtrace>(_ex))
Json::Reader().parse(*vmtraceJson, report["hints"]["vmtrace"]); Json::Reader().parse(*vmtraceJson, report["hints"]["vmtrace"]);
if (vector<bytes> const* receipts = boost::get_error_info<errinfo_receipts>(_ex))
{ 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"] = Json::arrayValue;
report["hints"]["receipts"].append(toHex(r)); for (auto const& r: *receipts)
} report["hints"]["receipts"].append(toHex(r));
if (h256Hash const* excluded = boost::get_error_info<errinfo_unclesExcluded>(_ex)) }
{ 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"] = Json::arrayValue;
report["hints"]["unclesExcluded"].append(Json::Value(r.hex())); for (auto const& r: h256Set() + *excluded)
} report["hints"]["unclesExcluded"].append(Json::Value(r.hex()));
}
#define DEV_HINT_ERRINFO(X) \ #define DEV_HINT_ERRINFO(X) \
if (auto const* n = boost::get_error_info<errinfo_ ## X>(_ex)) \ if (auto const* n = boost::get_error_info<errinfo_ ## X>(_ex)) \
report["hints"][#X] = toString(*n) report["hints"][#X] = toString(*n)
#define DEV_HINT_ERRINFO_HASH(X) \ #define DEV_HINT_ERRINFO_HASH(X) \
if (auto const* n = boost::get_error_info<errinfo_ ## X>(_ex)) \ if (auto const* n = boost::get_error_info<errinfo_ ## X>(_ex)) \
report["hints"][#X] = n->hex() report["hints"][#X] = n->hex()
DEV_HINT_ERRINFO_HASH(hash256); DEV_HINT_ERRINFO_HASH(hash256);
DEV_HINT_ERRINFO(uncleNumber); DEV_HINT_ERRINFO(uncleNumber);
DEV_HINT_ERRINFO(currentNumber); DEV_HINT_ERRINFO(currentNumber);
DEV_HINT_ERRINFO(now); DEV_HINT_ERRINFO(now);
DEV_HINT_ERRINFO(invalidSymbol); DEV_HINT_ERRINFO(invalidSymbol);
DEV_HINT_ERRINFO(wrongAddress); DEV_HINT_ERRINFO(wrongAddress);
DEV_HINT_ERRINFO(comment); DEV_HINT_ERRINFO(comment);
DEV_HINT_ERRINFO(min); DEV_HINT_ERRINFO(min);
DEV_HINT_ERRINFO(max); DEV_HINT_ERRINFO(max);
DEV_HINT_ERRINFO(name);
DEV_HINT_ERRINFO(required); DEV_HINT_ERRINFO(field);
DEV_HINT_ERRINFO(got); DEV_HINT_ERRINFO(data);
DEV_HINT_ERRINFO_HASH(required_LogBloom); DEV_HINT_ERRINFO_HASH(nonce);
DEV_HINT_ERRINFO_HASH(got_LogBloom); DEV_HINT_ERRINFO(difficulty);
DEV_HINT_ERRINFO_HASH(required_h256); DEV_HINT_ERRINFO(target);
DEV_HINT_ERRINFO_HASH(got_h256); 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); jsonrpc::HttpClient client(m_sentinel);
Sentinel rpc(client); Sentinel rpc(client);
try try
@ -276,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),
@ -415,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);
} }
@ -452,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)
@ -596,16 +611,26 @@ bool Client::submitWork(ProofOfWork::Solution const& _solution)
return true; return true;
} }
unsigned static const c_syncMin = 1;
unsigned static const c_syncMax = 100;
double static const c_targetDuration = 1;
void Client::syncBlockQueue() void Client::syncBlockQueue()
{ {
ImportRoute ir; ImportRoute ir;
cwork << "BQ ==> CHAIN ==> STATE"; cwork << "BQ ==> CHAIN ==> STATE";
{ boost::timer t;
tie(ir.first, ir.second, m_syncBlockQueue) = m_bc.sync(m_bq, m_stateDB, rand() % 90 + 10); tie(ir.first, ir.second, m_syncBlockQueue) = m_bc.sync(m_bq, m_stateDB, m_syncAmount);
if (ir.first.empty()) double elapsed = t.elapsed();
return;
} cnote << m_syncAmount << "blocks imported in" << unsigned(elapsed * 1000) << "ms (" << (m_syncAmount / elapsed) << "blocks/s)";
if (elapsed > c_targetDuration * 1.1 && m_syncAmount > c_syncMin)
m_syncAmount = max(c_syncMin, m_syncAmount * 9 / 10);
else if (elapsed < c_targetDuration * 0.9 && m_syncAmount < c_syncMax)
m_syncAmount = min(c_syncMax, m_syncAmount * 11 / 10 + 1);
if (ir.first.empty())
return;
onChainChanged(ir); onChainChanged(ir);
} }
@ -630,7 +655,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();
@ -673,47 +698,49 @@ 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
bool preChanged = false; if (!m_bq.items().first)
State newPreMine; {
DEV_READ_GUARDED(x_preMine) bool preChanged = false;
newPreMine = m_preMine; State newPreMine;
DEV_READ_GUARDED(x_preMine)
newPreMine = m_preMine;
// TODO: use m_postMine to avoid re-evaluating our own blocks. // TODO: use m_postMine to avoid re-evaluating our own blocks.
preChanged = newPreMine.sync(m_bc); preChanged = newPreMine.sync(m_bc);
if (preChanged || m_postMine.address() != m_preMine.address()) if (preChanged || m_postMine.address() != m_preMine.address())
{ {
if (isMining()) if (isMining())
cnote << "New block on chain."; cnote << "New block on chain.";
DEV_WRITE_GUARDED(x_preMine)
m_preMine = newPreMine;
DEV_WRITE_GUARDED(x_working)
m_working = newPreMine;
DEV_READ_GUARDED(x_postMine)
for (auto const& t: m_postMine.pending())
{
clog(ClientNote) << "Resubmitting post-mine transaction " << t;
auto ir = m_tq.import(t, TransactionQueue::ImportCallback(), IfDropped::Retry);
if (ir != ImportResult::Success)
onTransactionQueueReady();
}
DEV_READ_GUARDED(x_working) DEV_WRITE_GUARDED(x_postMine)
m_postMine = m_working;
DEV_WRITE_GUARDED(x_preMine) changeds.insert(PendingChangedFilter);
m_preMine = newPreMine;
DEV_WRITE_GUARDED(x_working)
m_working = newPreMine;
DEV_READ_GUARDED(x_postMine)
for (auto const& t: m_postMine.pending())
{
clog(ClientNote) << "Resubmitting post-mine transaction " << t;
auto ir = m_tq.import(t, TransactionQueue::ImportCallback(), IfDropped::Retry);
if (ir != ImportResult::Success)
onTransactionQueueReady();
}
DEV_READ_GUARDED(x_working) DEV_WRITE_GUARDED(x_postMine)
m_postMine = m_working;
changeds.insert(PendingChangedFilter); onPostStateChanged();
}
onPostStateChanged(); // Quick hack for now - the TQ at this point already has the prior pending transactions in it;
// we should resync with it manually until we are stricter about what constitutes "knowing".
onTransactionQueueReady();
} }
// Quick hack for now - the TQ at this point already has the prior pending transactions in it;
// we should resync with it manually until we are stricter about what constitutes "knowing".
onTransactionQueueReady();
noteChanged(changeds); noteChanged(changeds);
} }
@ -779,15 +806,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()
@ -847,7 +877,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()
@ -878,8 +917,8 @@ void Client::flushTransactions()
doWork(); doWork();
} }
HashChainStatus Client::hashChainStatus() const SyncStatus Client::syncStatus() const
{ {
auto h = m_host.lock(); auto h = m_host.lock();
return h ? h->status() : HashChainStatus { 0, 0, false }; return h ? h->status() : SyncStatus();
} }

6
libethereum/Client.h

@ -157,7 +157,7 @@ public:
/// 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. /// Get some information on the block queue.
HashChainStatus hashChainStatus() const; SyncStatus syncStatus() const;
/// Get the block queue. /// Get the block queue.
BlockQueue const& blockQueue() const { return m_bq; } BlockQueue const& blockQueue() const { return m_bq; }
@ -301,7 +301,7 @@ private:
/// Called when we have attempted to import a bad block. /// Called when we have attempted to import a bad block.
/// @warning May be called from any thread. /// @warning May be called from any thread.
void onBadBlock(Exception& _ex); 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.
@ -338,6 +338,8 @@ private:
mutable std::chrono::system_clock::time_point m_lastTick = std::chrono::system_clock::now(); mutable std::chrono::system_clock::time_point m_lastTick = std::chrono::system_clock::now();
///< When did we last tick()? ///< When did we last tick()?
unsigned m_syncAmount = 50; ///< Number of blocks to sync in each go.
ActivityReport m_report; ActivityReport m_report;
std::condition_variable m_signalled; std::condition_variable m_signalled;

51
libethereum/ClientBase.cpp

@ -45,19 +45,21 @@ State ClientBase::asOf(BlockNumber _h) const
return asOf(bc().numberHash(_h)); return asOf(bc().numberHash(_h));
} }
void ClientBase::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) void ClientBase::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, u256 _nonce)
{ {
prepareForTransaction(); prepareForTransaction();
auto a = toAddress(_secret); Transaction t(_value, _gasPrice, _gas, _dest, _data, _nonce, _secret);
u256 n = postMine().transactionsFrom(a);
cdebug << "submitTx: " << a << "postMine=" << n << "; tq=" << m_tq.maxNonce(a);
n = max<u256>(n, m_tq.maxNonce(a));
Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret);
m_tq.import(t.rlp()); m_tq.import(t.rlp());
StructuredLogger::transactionReceived(t.sha3().abridged(), t.sender().abridged()); StructuredLogger::transactionReceived(t.sha3().abridged(), t.sender().abridged());
cnote << "New transaction " << t << "(maxNonce for sender" << a << "is" << m_tq.maxNonce(a) << ")"; cnote << "New transaction " << t;
}
void ClientBase::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice)
{
auto a = toAddress(_secret);
submitTransaction(_secret, _value, _dest, _data, _gas, _gasPrice, max<u256>(postMine().transactionsFrom(a), m_tq.maxNonce(a)));
} }
Address ClientBase::submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice) Address ClientBase::submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice)
@ -171,8 +173,8 @@ LocalisedLogEntries ClientBase::logs(unsigned _watchId) const
LocalisedLogEntries ClientBase::logs(LogFilter const& _f) const LocalisedLogEntries ClientBase::logs(LogFilter const& _f) const
{ {
LocalisedLogEntries ret; LocalisedLogEntries ret;
unsigned begin = min<unsigned>(bc().number() + 1, (unsigned)_f.latest()); unsigned begin = min(bc().number() + 1, (unsigned)numberFromHash(_f.latest()));
unsigned end = min(bc().number(), min(begin, (unsigned)_f.earliest())); unsigned end = min(bc().number(), min(begin, (unsigned)numberFromHash(_f.earliest())));
// Handle pending transactions differently as they're not on the block chain. // Handle pending transactions differently as they're not on the block chain.
if (begin > bc().number()) if (begin > bc().number())
@ -182,11 +184,10 @@ LocalisedLogEntries ClientBase::logs(LogFilter const& _f) const
{ {
// Might have a transaction that contains a matching log. // Might have a transaction that contains a matching log.
TransactionReceipt const& tr = temp.receipt(i); TransactionReceipt const& tr = temp.receipt(i);
auto th = temp.pending()[i].sha3();
LogEntries le = _f.matches(tr); LogEntries le = _f.matches(tr);
if (le.size()) if (le.size())
for (unsigned j = 0; j < le.size(); ++j) for (unsigned j = 0; j < le.size(); ++j)
ret.insert(ret.begin(), LocalisedLogEntry(le[j], begin, th)); ret.insert(ret.begin(), LocalisedLogEntry(le[j]));
} }
begin = bc().number(); begin = bc().number();
} }
@ -201,20 +202,22 @@ LocalisedLogEntries ClientBase::logs(LogFilter const& _f) const
{ {
int total = 0; int total = 0;
auto h = bc().numberHash(n); auto h = bc().numberHash(n);
auto info = bc().info(h);
auto receipts = bc().receipts(h).receipts; auto receipts = bc().receipts(h).receipts;
unsigned logIndex = 0;
for (size_t i = 0; i < receipts.size(); i++) for (size_t i = 0; i < receipts.size(); i++)
{ {
logIndex++;
TransactionReceipt receipt = receipts[i]; TransactionReceipt receipt = receipts[i];
if (_f.matches(receipt.bloom())) if (_f.matches(receipt.bloom()))
{ {
auto info = bc().info(h);
auto th = transaction(info.hash(), i).sha3(); auto th = transaction(info.hash(), i).sha3();
LogEntries le = _f.matches(receipt); LogEntries le = _f.matches(receipt);
if (le.size()) if (le.size())
{ {
total += le.size(); total += le.size();
for (unsigned j = 0; j < le.size(); ++j) for (unsigned j = 0; j < le.size(); ++j)
ret.insert(ret.begin(), LocalisedLogEntry(le[j], n, th)); ret.insert(ret.begin(), LocalisedLogEntry(le[j], info, th, i, logIndex));
} }
} }
@ -313,6 +316,8 @@ LocalisedLogEntries ClientBase::checkWatch(unsigned _watchId)
BlockInfo ClientBase::blockInfo(h256 _hash) const BlockInfo ClientBase::blockInfo(h256 _hash) const
{ {
if (_hash == PendingBlockHash)
return preMine().info();
return BlockInfo(bc().block(_hash)); return BlockInfo(bc().block(_hash));
} }
@ -441,6 +446,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;
}

6
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
{ {
@ -76,6 +76,7 @@ public:
virtual ~ClientBase() {} virtual ~ClientBase() {}
/// Submits the given message-call transaction. /// Submits the given message-call transaction.
virtual void submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, u256 _nonce);
virtual void submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo) override; virtual void submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo) override;
/// Submits a new contract-creation transaction. /// Submits a new contract-creation transaction.
@ -117,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;
@ -175,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.
}; };

29
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.
@ -77,18 +77,27 @@ enum class Asking
Nothing Nothing
}; };
enum class Syncing enum class SyncState
{ {
Waiting, Idle, ///< Initial chain sync complete. Waiting for new packets
Executing, WaitingQueue, ///< Block downloading paused. Waiting for block queue to process blocks and free space
Done HashesNegotiate, ///< Waiting for first hashes to arrive
HashesSingle, ///< Locked on and downloading hashes from a single peer
HashesParallel, ///< Downloading hashes from multiple peers over
Blocks, ///< Downloading blocks
NewBlocks, ///< Downloading blocks learned from NewHashes packet
Size /// Must be kept last
}; };
struct HashChainStatus struct SyncStatus
{ {
unsigned total; SyncState state = SyncState::Idle;
unsigned received; unsigned hashesTotal = 0;
bool estimated; unsigned hashesReceived = 0;
bool hashesEstimated = false;
unsigned blocksTotal = 0;
unsigned blocksReceived = 0;
}; };
} }

5
libethereum/DownloadMan.h

@ -253,11 +253,6 @@ 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); }

243
libethereum/EthereumHost.cpp

@ -39,6 +39,9 @@ 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;
char const* const EthereumHost::s_stateNames[static_cast<int>(SyncState::Size)] = {"Idle", "WaitingQueue", "HashesNegotiate", "HashesSingle", "HashesParallel", "Blocks", "NewBlocks" };
EthereumHost::EthereumHost(BlockChain const& _ch, TransactionQueue& _tq, BlockQueue& _bq, u256 _networkId): EthereumHost::EthereumHost(BlockChain const& _ch, TransactionQueue& _tq, BlockQueue& _bq, u256 _networkId):
HostCapability<EthereumPeer>(), HostCapability<EthereumPeer>(),
@ -48,8 +51,10 @@ EthereumHost::EthereumHost(BlockChain const& _ch, TransactionQueue& _tq, BlockQu
m_bq (_bq), m_bq (_bq),
m_networkId (_networkId) m_networkId (_networkId)
{ {
setState(SyncState::HashesNegotiate);
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()
@ -77,8 +82,7 @@ void EthereumHost::reset()
foreachPeer([](EthereumPeer* _p) { _p->abortSync(); }); foreachPeer([](EthereumPeer* _p) { _p->abortSync(); });
m_man.resetToChain(h256s()); m_man.resetToChain(h256s());
m_hashMan.reset(m_chain.number() + 1); m_hashMan.reset(m_chain.number() + 1);
m_needSyncBlocks = true; setState(SyncState::HashesNegotiate);
m_needSyncHashes = true;
m_syncingLatestHash = h256(); m_syncingLatestHash = h256();
m_syncingTotalDifficulty = 0; m_syncingTotalDifficulty = 0;
m_latestBlockSent = h256(); m_latestBlockSent = h256();
@ -86,12 +90,28 @@ void EthereumHost::reset()
m_hashes.clear(); m_hashes.clear();
} }
void EthereumHost::resetSyncTo(h256 const& _h)
{
setState(SyncState::HashesNegotiate);
m_syncingLatestHash = _h;
}
void EthereumHost::setState(SyncState _s)
{
if (m_state != _s)
{
clog(NetAllDetail) << "SyncState changed from " << stateName(m_state) << " to " << stateName(_s);
m_state = _s;
}
}
void EthereumHost::doWork() 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 +125,13 @@ void EthereumHost::doWork()
} }
} }
if (m_continueSync)
{
m_continueSync = false;
RecursiveGuard l(x_sync);
continueSync();
}
foreachPeer([](EthereumPeer* _p) { _p->tick(); }); foreachPeer([](EthereumPeer* _p) { _p->tick(); });
// return netChange; // return netChange;
@ -144,6 +171,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;
}); });
@ -238,6 +266,7 @@ void EthereumHost::maintainBlocks(h256 const& _currentHash)
void EthereumHost::onPeerStatus(EthereumPeer* _peer) void EthereumHost::onPeerStatus(EthereumPeer* _peer)
{ {
RecursiveGuard l(x_sync); RecursiveGuard l(x_sync);
DEV_INVARIANT_CHECK;
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,49 +279,50 @@ void EthereumHost::onPeerStatus(EthereumPeer* _peer)
_peer->disable("Peer banned for previous bad behaviour."); _peer->disable("Peer banned for previous bad behaviour.");
else else
{ {
if (_peer->m_protocolVersion != protocolVersion()) unsigned estimatedHashes = estimateHashes();
estimatePeerHashes(_peer); if (_peer->m_protocolVersion == protocolVersion())
else
{ {
if (_peer->m_latestBlockNumber > m_chain.number()) if (_peer->m_latestBlockNumber > m_chain.number())
_peer->m_expectedHashes = (unsigned)_peer->m_latestBlockNumber - m_chain.number(); _peer->m_expectedHashes = (unsigned)_peer->m_latestBlockNumber - m_chain.number();
if (m_hashMan.chainSize() < _peer->m_expectedHashes) if (_peer->m_expectedHashes > estimatedHashes)
_peer->disable("Too many hashes");
else if (needHashes() && m_hashMan.chainSize() < _peer->m_expectedHashes)
m_hashMan.resetToRange(m_chain.number() + 1, _peer->m_expectedHashes); m_hashMan.resetToRange(m_chain.number() + 1, _peer->m_expectedHashes);
} }
else
_peer->m_expectedHashes = estimatedHashes;
continueSync(_peer); continueSync(_peer);
} }
DEV_INVARIANT_CHECK;
} }
void EthereumHost::estimatePeerHashes(EthereumPeer* _peer) unsigned EthereumHost::estimateHashes()
{ {
BlockInfo block = m_chain.info(); BlockInfo block = m_chain.info();
time_t lastBlockTime = (block.hash() == m_chain.genesisHash()) ? 1428192000 : (time_t)block.timestamp; time_t lastBlockTime = (block.hash() == m_chain.genesisHash()) ? 1428192000 : (time_t)block.timestamp;
time_t now = time(0); time_t now = time(0);
unsigned blockCount = 30000; unsigned blockCount = c_chainReorgSize;
if (lastBlockTime > now) if (lastBlockTime > now)
clog(NetWarn) << "Clock skew? Latest block is in the future"; clog(NetWarn) << "Clock skew? Latest block is in the future";
else else
blockCount += (now - lastBlockTime) / (unsigned)c_durationLimit; blockCount += (now - lastBlockTime) / (unsigned)c_durationLimit;
clog(NetAllDetail) << "Estimated hashes: " << blockCount; clog(NetAllDetail) << "Estimated hashes: " << blockCount;
_peer->m_expectedHashes = blockCount; return blockCount;
}
void EthereumHost::noteRude(p2p::NodeId const& _id, std::string const& _client)
{
cwarn << "RUDE node/client: " << _id << _client;
m_rudeNodes.insert(_id);
m_rudeClients.insert(_client);
} }
void EthereumHost::onPeerHashes(EthereumPeer* _peer, h256s const& _hashes) void EthereumHost::onPeerHashes(EthereumPeer* _peer, h256s const& _hashes)
{ {
RecursiveGuard l(x_sync); RecursiveGuard l(x_sync);
assert(_peer->m_asking == Asking::Nothing); if (_peer->m_syncHashNumber > 0)
_peer->m_syncHashNumber += _hashes.size();
_peer->setAsking(Asking::Nothing);
onPeerHashes(_peer, _hashes, false); onPeerHashes(_peer, _hashes, false);
} }
void EthereumHost::onPeerHashes(EthereumPeer* _peer, h256s const& _hashes, bool _complete) void EthereumHost::onPeerHashes(EthereumPeer* _peer, h256s const& _hashes, bool _complete)
{ {
DEV_INVARIANT_CHECK;
if (_hashes.empty()) if (_hashes.empty())
{ {
_peer->m_hashSub.doneFetch(); _peer->m_hashSub.doneFetch();
@ -301,7 +331,7 @@ void EthereumHost::onPeerHashes(EthereumPeer* _peer, h256s const& _hashes, bool
} }
bool syncByNumber = _peer->m_syncHashNumber; bool syncByNumber = _peer->m_syncHashNumber;
if (!syncByNumber && _peer->m_syncHash != m_syncingLatestHash) if (!syncByNumber && !_complete && _peer->m_syncHash != m_syncingLatestHash)
{ {
// Obsolete hashes, discard // Obsolete hashes, discard
continueSync(_peer); continueSync(_peer);
@ -359,47 +389,53 @@ void EthereumHost::onPeerHashes(EthereumPeer* _peer, h256s const& _hashes, bool
} }
if (_complete) if (_complete)
{ {
m_needSyncBlocks = true; clog(NetMessageSummary) << "Start new blocks download...";
m_syncingLatestHash = h256();
setState(SyncState::NewBlocks);
m_man.resetToChain(m_hashes);
m_hashes.clear();
m_hashMan.reset(m_chain.number() + 1);
continueSync(_peer); continueSync(_peer);
} }
else if (syncByNumber && m_hashMan.isComplete()) else if (syncByNumber && m_hashMan.isComplete())
{ {
// Done our chain-get. // Done our chain-get.
m_needSyncHashes = false;
clog(NetNote) << "Hashes download complete."; clog(NetNote) << "Hashes download complete.";
// 1/100th for each useful block hash. onPeerDoneHashes(_peer, false);
_peer->addRating(m_man.chainSize() / 100); //TODO: what about other peers?
m_hashMan.reset(m_chain.number() + 1);
continueSync();
} }
else if (m_hashes.size() > _peer->m_expectedHashes) else if (m_hashes.size() > _peer->m_expectedHashes)
{ {
_peer->disable("Too many hashes"); _peer->disable("Too many hashes");
m_hashes.clear(); m_hashes.clear();
m_syncingLatestHash = h256(); m_syncingLatestHash = h256();
setState(SyncState::HashesNegotiate);
continueSync(); ///Try with some other peer, keep the chain continueSync(); ///Try with some other peer, keep the chain
} }
else else
continueSync(_peer); /// Grab next hashes continueSync(_peer); /// Grab next hashes
DEV_INVARIANT_CHECK;
} }
void EthereumHost::onPeerDoneHashes(EthereumPeer* _peer, bool _localChain) void EthereumHost::onPeerDoneHashes(EthereumPeer* _peer, bool _localChain)
{ {
assert(_peer->m_asking == Asking::Nothing); assert(_peer->m_asking == Asking::Nothing);
m_needSyncHashes = false; m_syncingLatestHash = h256();
setState(SyncState::Blocks);
if (_peer->m_protocolVersion != protocolVersion() || _localChain) if (_peer->m_protocolVersion != protocolVersion() || _localChain)
{ {
m_man.resetToChain(m_hashes); m_man.resetToChain(m_hashes);
m_hashes.clear(); _peer->addRating(m_man.chainSize() / 100); //TODO: what about other peers?
m_hashMan.reset(m_chain.number() + 1);
} }
m_hashMan.reset(m_chain.number() + 1);
m_hashes.clear();
continueSync(); continueSync();
} }
void EthereumHost::onPeerBlocks(EthereumPeer* _peer, RLP const& _r) void EthereumHost::onPeerBlocks(EthereumPeer* _peer, RLP const& _r)
{ {
RecursiveGuard l(x_sync); RecursiveGuard l(x_sync);
assert(_peer->m_asking == Asking::Nothing); DEV_INVARIANT_CHECK;
_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");
@ -418,6 +454,7 @@ void EthereumHost::onPeerBlocks(EthereumPeer* _peer, RLP const& _r)
unsigned unknown = 0; unsigned unknown = 0;
unsigned got = 0; unsigned got = 0;
unsigned repeated = 0; unsigned repeated = 0;
h256 lastUnknown;
for (unsigned i = 0; i < itemCount; ++i) for (unsigned i = 0; i < itemCount; ++i)
{ {
@ -446,6 +483,7 @@ void EthereumHost::onPeerBlocks(EthereumPeer* _peer, RLP const& _r)
break; break;
case ImportResult::UnknownParent: case ImportResult::UnknownParent:
lastUnknown = h;
unknown++; unknown++;
break; break;
@ -461,34 +499,36 @@ 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) if (m_state == SyncState::NewBlocks && unknown > 0)
{ {
// Done our chain-get. _peer->m_latestHash = lastUnknown;
m_needSyncBlocks = false; resetSyncTo(lastUnknown);
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);
DEV_INVARIANT_CHECK;
} }
void EthereumHost::onPeerNewHashes(EthereumPeer* _peer, h256s const& _hashes) void EthereumHost::onPeerNewHashes(EthereumPeer* _peer, h256s const& _hashes)
{ {
RecursiveGuard l(x_sync); RecursiveGuard l(x_sync);
if (isSyncing_UNSAFE()) DEV_INVARIANT_CHECK;
if (isSyncing() || _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);
DEV_INVARIANT_CHECK;
} }
void EthereumHost::onPeerNewBlock(EthereumPeer* _peer, RLP const& _r) void EthereumHost::onPeerNewBlock(EthereumPeer* _peer, RLP const& _r)
{ {
RecursiveGuard l(x_sync); RecursiveGuard l(x_sync);
if (isSyncing_UNSAFE()) DEV_INVARIANT_CHECK;
if ((isSyncing() || _peer->isConversing()) && m_state != SyncState::NewBlocks)
{ {
clog(NetMessageSummary) << "Ignoring new blocks since we're already downloading."; clog(NetMessageSummary) << "Ignoring new blocks since we're already downloading.";
return; return;
@ -528,9 +568,7 @@ void EthereumHost::onPeerNewBlock(EthereumPeer* _peer, RLP const& _r)
clog(NetMessageSummary) << "Received block with no known parent. Resyncing..."; clog(NetMessageSummary) << "Received block with no known parent. Resyncing...";
_peer->m_latestHash = h; _peer->m_latestHash = h;
_peer->m_totalDifficulty = difficulty; _peer->m_totalDifficulty = difficulty;
m_needSyncHashes = true; resetSyncTo(h);;
m_needSyncBlocks = true;
m_syncingLatestHash = h;
sync = true; sync = true;
} }
} }
@ -542,8 +580,9 @@ 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();
} }
DEV_INVARIANT_CHECK;
} }
void EthereumHost::onPeerTransactions(EthereumPeer* _peer, RLP const& _r) void EthereumHost::onPeerTransactions(EthereumPeer* _peer, RLP const& _r)
@ -577,17 +616,20 @@ void EthereumHost::onPeerTransactions(EthereumPeer* _peer, RLP const& _r)
void EthereumHost::onPeerAborting(EthereumPeer* _peer) void EthereumHost::onPeerAborting(EthereumPeer* _peer)
{ {
RecursiveGuard l(x_sync); RecursiveGuard l(x_sync);
if (_peer->isSyncing()) if (_peer->isConversing())
{ {
_peer->setIdle(); _peer->setIdle();
_peer->setRude(); if (_peer->isCriticalSyncing())
_peer->setRude();
continueSync(); continueSync();
} }
} }
void EthereumHost::continueSync() void EthereumHost::continueSync()
{ {
clog(NetAllDetail) << "Getting help with downloading hashes and blocks"; if (m_state == SyncState::WaitingQueue)
setState(m_lastActiveState);
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)
@ -597,11 +639,18 @@ void EthereumHost::continueSync()
void EthereumHost::continueSync(EthereumPeer* _peer) void EthereumHost::continueSync(EthereumPeer* _peer)
{ {
DEV_INVARIANT_CHECK;
assert(_peer->m_asking == Asking::Nothing); assert(_peer->m_asking == Asking::Nothing);
bool otherPeerV60Sync = false; bool otherPeerV60Sync = false;
bool otherPeerV61Sync = false; bool otherPeerV61Sync = false;
if (m_needSyncHashes && peerShouldGrabChain(_peer)) if (needHashes())
{ {
if (!peerShouldGrabChain(_peer))
{
_peer->setIdle();
return;
}
foreachPeer([&](EthereumPeer* _p) foreachPeer([&](EthereumPeer* _p)
{ {
if (_p != _peer && _p->m_asking == Asking::Hashes) if (_p != _peer && _p->m_asking == Asking::Hashes)
@ -626,7 +675,7 @@ void EthereumHost::continueSync(EthereumPeer* _peer)
} }
if (_peer->m_protocolVersion == protocolVersion() && !m_hashMan.isComplete()) if (_peer->m_protocolVersion == protocolVersion() && !m_hashMan.isComplete())
{ {
m_syncingV61 = true; setState(SyncState::HashesParallel);
_peer->requestHashes(); /// v61+ and not catching up to a particular hash _peer->requestHashes(); /// v61+ and not catching up to a particular hash
} }
else else
@ -640,25 +689,63 @@ void EthereumHost::continueSync(EthereumPeer* _peer)
if (_peer->m_totalDifficulty >= m_syncingTotalDifficulty) if (_peer->m_totalDifficulty >= m_syncingTotalDifficulty)
{ {
_peer->requestHashes(m_syncingLatestHash); _peer->requestHashes(m_syncingLatestHash);
m_syncingV61 = false; setState(SyncState::HashesSingle);
m_estimatedHashes = _peer->m_expectedHashes; m_estimatedHashes = _peer->m_expectedHashes - (_peer->m_protocolVersion == protocolVersion() ? 0 : c_chainReorgSize);
} }
else else
_peer->setIdle(); _peer->setIdle();
} }
} }
else if (m_needSyncBlocks && peerShouldGrabBlocks(_peer)) // Check if this peer can help with downloading blocks else if (needBlocks())
_peer->requestBlocks(); {
if (m_man.isComplete())
{
// Done our chain-get.
setState(SyncState::Idle);
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";
m_lastActiveState = m_state;
setState(SyncState::WaitingQueue);
_peer->setIdle();
}
else
_peer->requestBlocks();
}
}
else else
_peer->setIdle(); _peer->setIdle();
DEV_INVARIANT_CHECK;
} }
bool EthereumHost::peerShouldGrabBlocks(EthereumPeer* _peer) const bool EthereumHost::peerCanHelp(EthereumPeer* _peer) const
{ {
// Early exit if this peer has proved unreliable. (void)_peer;
if (_peer->isRude()) return true;
return false; }
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;
@ -692,24 +779,42 @@ bool EthereumHost::peerShouldGrabChain(EthereumPeer* _peer) const
} }
} }
bool EthereumHost::isSyncing_UNSAFE() const bool EthereumHost::isSyncing() 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_state != SyncState::Idle;
/// 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() SyncStatus EthereumHost::status() const
{ {
RecursiveGuard l(x_sync); RecursiveGuard l(x_sync);
if (m_syncingV61) SyncStatus res;
return HashChainStatus { static_cast<unsigned>(m_hashMan.chainSize()), static_cast<unsigned>(m_hashMan.gotCount()), false }; res.state = m_state;
return HashChainStatus { m_estimatedHashes, static_cast<unsigned>(m_hashes.size()), true }; if (m_state == SyncState::HashesParallel)
{
res.hashesReceived = m_hashMan.hashesGot().size();
res.hashesTotal = m_hashMan.chainSize();
}
else if (m_state == SyncState::HashesSingle)
{
res.hashesTotal = m_estimatedHashes;
res.hashesReceived = static_cast<unsigned>(m_hashes.size());
res.hashesEstimated = true;
}
else if (m_state == SyncState::Blocks || m_state == SyncState::NewBlocks || m_state == SyncState::WaitingQueue)
{
res.blocksTotal = m_man.chainSize();
res.blocksReceived = m_man.blocksGot().size();
}
return res;
} }
bool EthereumHost::invariants() const
{
if (m_state == SyncState::HashesNegotiate && !m_hashes.empty())
return false;
if (needBlocks() && (m_syncingLatestHash || !m_hashes.empty()))
return false;
return true;
}

38
libethereum/EthereumHost.h

@ -54,9 +54,10 @@ class BlockQueue;
* @warning None of this is thread-safe. You have been warned. * @warning None of this is thread-safe. You have been warned.
* @doWork Syncs to peers and sends new blocks and transactions. * @doWork Syncs to peers and sends new blocks and transactions.
*/ */
class EthereumHost: public p2p::HostCapability<EthereumPeer>, Worker class EthereumHost: public p2p::HostCapability<EthereumPeer>, Worker, HasInvariants
{ {
public: public:
/// Start server, but don't listen. /// Start server, but don't listen.
EthereumHost(BlockChain const& _ch, TransactionQueue& _tq, BlockQueue& _bq, u256 _networkId); EthereumHost(BlockChain const& _ch, TransactionQueue& _tq, BlockQueue& _bq, u256 _networkId);
@ -70,10 +71,8 @@ public:
void reset(); void reset();
DownloadMan const& downloadMan() const { return m_man; } DownloadMan const& downloadMan() const { return m_man; }
bool isSyncing() const { RecursiveGuard l(x_sync); return isSyncing_UNSAFE(); } bool isSyncing() const;
bool isBanned(p2p::NodeId const& _id) const { return !!m_banned.count(_id); } bool isBanned(p2p::NodeId const& _id) const { return !!m_banned.count(_id); }
void noteRude(p2p::NodeId const& _id, std::string const& _client);
bool isRude(p2p::NodeId const& _id, std::string const& _client) const { return m_rudeClients.count(_client) && m_rudeNodes.count(_id); }
void noteNewTransactions() { m_newTransactions = true; } void noteNewTransactions() { m_newTransactions = true; }
void noteNewBlocks() { m_newBlocks = true; } void noteNewBlocks() { m_newBlocks = true; }
@ -89,16 +88,21 @@ public:
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(); SyncStatus status() const;
static char const* stateName(SyncState _s) { return s_stateNames[static_cast<int>(_s)]; }
static unsigned const c_oldProtocolVersion; static unsigned const c_oldProtocolVersion;
private: private:
static char const* const s_stateNames[static_cast<int>(SyncState::Size)];
std::tuple<std::vector<std::shared_ptr<EthereumPeer>>, std::vector<std::shared_ptr<EthereumPeer>>, std::vector<std::shared_ptr<p2p::Session>>> randomSelection(unsigned _percent = 25, std::function<bool(EthereumPeer*)> const& _allow = [](EthereumPeer const*){ return true; }); std::tuple<std::vector<std::shared_ptr<EthereumPeer>>, std::vector<std::shared_ptr<EthereumPeer>>, std::vector<std::shared_ptr<p2p::Session>>> randomSelection(unsigned _percent = 25, std::function<bool(EthereumPeer*)> const& _allow = [](EthereumPeer const*){ return true; });
void foreachPeerPtr(std::function<void(std::shared_ptr<EthereumPeer>)> const& _f) const; void foreachPeerPtr(std::function<void(std::shared_ptr<EthereumPeer>)> const& _f) const;
void foreachPeer(std::function<void(EthereumPeer*)> const& _f) const; void foreachPeer(std::function<void(EthereumPeer*)> const& _f) const;
bool isSyncing_UNSAFE() const; void resetSyncTo(h256 const& _h);
bool needHashes() const { return m_state == SyncState::HashesNegotiate || m_state == SyncState::HashesSingle || m_state == SyncState::HashesParallel; }
bool needBlocks() const { return m_state == SyncState::Blocks || m_state == SyncState::NewBlocks; }
/// Sync with the BlockChain. It might contain one of our mined blocks, we might have new candidates from the network. /// Sync with the BlockChain. It might contain one of our mined blocks, we might have new candidates from the network.
void doWork(); void doWork();
@ -126,11 +130,17 @@ 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);
void setState(SyncState _s);
bool invariants() const override;
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;
@ -146,15 +156,13 @@ private:
bool m_newBlocks = false; bool m_newBlocks = false;
mutable RecursiveMutex x_sync; mutable RecursiveMutex x_sync;
bool m_needSyncHashes = true; ///< Indicates if need to downlad hashes SyncState m_state = SyncState::Idle; ///< Current sync state
bool m_needSyncBlocks = true; ///< Indicates if we still need to download some blocks SyncState m_lastActiveState = SyncState::Idle; ///< Saved state before entering waiting queue mode
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 PV60 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. 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.
std::unordered_set<p2p::NodeId> m_rudeNodes; ///< Nodes that were impolite while syncing. We avoid syncing from these if possible.
std::unordered_set<std::string> m_rudeClients; ///< Nodes that were impolite while syncing. We avoid syncing from these if possible.
}; };
} }

35
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,8 +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)
{ {
m_isRude = host()->isRude(session()->id(), session()->info().clientVersion); session()->addNote("manners", isRude() ? "RUDE" : "nice");
session()->addNote("manners", m_isRude ? "RUDE" : "nice");
m_syncHashNumber = host()->chain().number() + 1; m_syncHashNumber = host()->chain().number() + 1;
requestStatus(); requestStatus();
} }
@ -52,16 +52,20 @@ EthereumPeer::~EthereumPeer()
abortSync(); abortSync();
} }
void EthereumPeer::abortSync() bool EthereumPeer::isRude() const
{ {
host()->onPeerAborting(this); return repMan().isRude(*session(), name());
} }
void EthereumPeer::setRude() void EthereumPeer::setRude()
{ {
m_isRude = true; repMan().noteRude(*session(), name());
host()->noteRude(session()->id(), session()->info().clientVersion); session()->addNote("manners", "RUDE");
session()->addNote("manners", m_isRude ? "RUDE" : "nice"); }
void EthereumPeer::abortSync()
{
host()->onPeerAborting(this);
} }
EthereumHost* EthereumPeer::host() const EthereumHost* EthereumPeer::host() const
@ -136,7 +140,7 @@ void EthereumPeer::requestHashes(h256 const& _lastHash)
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;
@ -156,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()
@ -166,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
@ -256,13 +265,10 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
clog(NetWarn) << "Peer giving us hashes when we didn't ask for them."; clog(NetWarn) << "Peer giving us hashes when we didn't ask for them.";
break; break;
} }
setAsking(Asking::Nothing);
h256s hashes(itemCount); h256s hashes(itemCount);
for (unsigned i = 0; i < itemCount; ++i) for (unsigned i = 0; i < itemCount; ++i)
hashes[i] = _r[i].toHash<h256>(); hashes[i] = _r[i].toHash<h256>();
if (m_syncHashNumber > 0)
m_syncHashNumber += itemCount;
host()->onPeerHashes(this, hashes); host()->onPeerHashes(this, hashes);
break; break;
} }
@ -305,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:

11
libethereum/EthereumPeer.h

@ -83,7 +83,7 @@ public:
void requestBlocks(); void requestBlocks();
/// Check if this node is rude. /// Check if this node is rude.
bool isRude() const { return m_isRude; } bool isRude() const;
/// Set that it's a rude node. /// Set that it's a rude node.
void setRude(); void setRude();
@ -109,8 +109,11 @@ private:
/// Do we presently need syncing with this peer? /// Do we presently need syncing with this peer?
bool needsSyncing() const { return !isRude() && !!m_latestHash; } bool needsSyncing() const { return !isRude() && !!m_latestHash; }
/// Are we presently syncing with this peer? /// Are we presently in the process of communicating with this peer?
bool isSyncing() const; bool isConversing() const;
/// Are we presently in a critical part of the syncing process with this peer?
bool isCriticalSyncing() const;
/// Runs period checks to check up on the peer. /// Runs period checks to check up on the peer.
void tick(); void tick();
@ -152,8 +155,6 @@ private:
h256Hash m_knownBlocks; ///< Blocks that the peer already knows about (that don't need to be sent to them). h256Hash m_knownBlocks; ///< Blocks that the peer already knows about (that don't need to be sent to them).
Mutex x_knownTransactions; Mutex x_knownTransactions;
h256Hash m_knownTransactions; ///< Transactions that the peer already knows of. h256Hash m_knownTransactions; ///< Transactions that the peer already knows of.
bool m_isRude; ///< True if this node has been rude in the past.
}; };
} }

41
libethereum/Executive.cpp

@ -46,6 +46,8 @@ bool changesMemory(Instruction _inst)
return return
_inst == Instruction::MSTORE || _inst == Instruction::MSTORE ||
_inst == Instruction::MSTORE8 || _inst == Instruction::MSTORE8 ||
_inst == Instruction::MLOAD ||
_inst == Instruction::CREATE ||
_inst == Instruction::CALL || _inst == Instruction::CALL ||
_inst == Instruction::CALLCODE || _inst == Instruction::CALLCODE ||
_inst == Instruction::SHA3 || _inst == Instruction::SHA3 ||
@ -71,6 +73,7 @@ void StandardTrace::operator()(uint64_t _steps, Instruction inst, bigint newMemS
stack.append(toHex(toCompactBigEndian(i), 1)); stack.append(toHex(toCompactBigEndian(i), 1));
r["stack"] = stack; r["stack"] = stack;
bool returned = false;
bool newContext = false; bool newContext = false;
Instruction lastInst = Instruction::STOP; Instruction lastInst = Instruction::STOP;
@ -84,6 +87,7 @@ void StandardTrace::operator()(uint64_t _steps, Instruction inst, bigint newMemS
else if (m_lastInst.size() == ext.depth + 2) else if (m_lastInst.size() == ext.depth + 2)
{ {
// returned from old context // returned from old context
returned = true;
m_lastInst.pop_back(); m_lastInst.pop_back();
lastInst = m_lastInst.back(); lastInst = m_lastInst.back();
} }
@ -91,6 +95,7 @@ void StandardTrace::operator()(uint64_t _steps, Instruction inst, bigint newMemS
{ {
// continuing in previous context // continuing in previous context
lastInst = m_lastInst.back(); lastInst = m_lastInst.back();
m_lastInst.back() = inst;
} }
else else
{ {
@ -101,10 +106,10 @@ void StandardTrace::operator()(uint64_t _steps, Instruction inst, bigint newMemS
if (changesMemory(lastInst) || newContext) if (changesMemory(lastInst) || newContext)
{ {
Json::Value mem(Json::arrayValue); if (vm.memory().size() < 1024)
for (auto const& i: vm.memory()) r["memory"] = toHex(vm.memory());
mem.append(toHex(toCompactBigEndian(i), 1)); else
r["memory"] = mem; r["sha3memory"] = sha3(vm.memory()).hex();
} }
if (changesStorage(lastInst) || newContext) if (changesStorage(lastInst) || newContext)
@ -115,21 +120,26 @@ void StandardTrace::operator()(uint64_t _steps, Instruction inst, bigint newMemS
r["storage"] = storage; r["storage"] = storage;
} }
r["depth"] = ext.depth; if (returned || newContext)
r["address"] = ext.myAddress.hex(); r["depth"] = ext.depth;
if (newContext)
r["address"] = ext.myAddress.hex();
r["steps"] = (unsigned)_steps; r["steps"] = (unsigned)_steps;
r["inst"] = (unsigned)inst; r["inst"] = (unsigned)inst;
if (m_showMnemonics)
r["instname"] = instructionInfo(inst).name;
r["pc"] = toString(vm.curPC()); r["pc"] = toString(vm.curPC());
r["gas"] = toString(gas); r["gas"] = toString(gas);
r["gascost"] = toString(gasCost); r["gascost"] = toString(gasCost);
r["memexpand"] = toString(newMemSize); if (!!newMemSize)
r["memexpand"] = toString(newMemSize);
m_trace->append(r); m_trace->append(r);
} }
string StandardTrace::json() const string StandardTrace::json(bool _styled) const
{ {
return Json::FastWriter().write(*m_trace); 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):
@ -191,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()));
} }
} }
@ -249,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);
} }
} }
@ -269,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);

11
libethereum/Executive.h

@ -49,9 +49,12 @@ public:
StandardTrace(); StandardTrace();
void operator()(uint64_t _steps, Instruction _inst, bigint _newMemSize, bigint _gasCost, bigint _gas, VM* _vm, ExtVMFace const* _extVM); void operator()(uint64_t _steps, Instruction _inst, bigint _newMemSize, bigint _gasCost, bigint _gas, VM* _vm, ExtVMFace const* _extVM);
std::string json() const; void setShowMnemonics() { m_showMnemonics = true; }
std::string json(bool _styled = false) const;
private: private:
bool m_showMnemonics = false;
std::vector<Instruction> m_lastInst; std::vector<Instruction> m_lastInst;
std::shared_ptr<Json::Value> m_trace; std::shared_ptr<Json::Value> m_trace;
}; };
@ -83,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;
@ -142,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.
@ -155,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;
}; };
} }

59
libethereum/ExtVM.cpp

@ -20,18 +20,69 @@
*/ */
#include "ExtVM.h" #include "ExtVM.h"
#include <exception>
#include <boost/thread.hpp>
#include "Executive.h" #include "Executive.h"
using namespace std;
using namespace dev; using namespace dev;
using namespace dev::eth; using namespace dev::eth;
namespace
{
static unsigned const c_depthLimit = 1024;
/// Upper bound of stack space needed by single CALL/CREATE execution. Set experimentally.
static size_t const c_singleExecutionStackSize = 12 * 1024;
/// Standard OSX thread stack limit. Should be reasonable for other platforms too.
static size_t const c_defaultStackSize = 512 * 1024;
/// On what depth execution should be offloaded to additional separated stack space.
static unsigned const c_offloadPoint = c_defaultStackSize / c_singleExecutionStackSize;
void goOnOffloadedStack(Executive& _e, OnOpFunc const& _onOp)
{
// Set new stack size enouth to handle the rest of the calls up to the limit.
boost::thread::attributes attrs;
attrs.set_stack_size((c_depthLimit - c_offloadPoint) * c_singleExecutionStackSize);
// Create new thread with big stack and join immediately.
// TODO: It is possible to switch the implementation to Boost.Context or similar when the API is stable.
std::exception_ptr exception;
boost::thread{attrs, [&]{
try
{
_e.go(_onOp);
}
catch (...)
{
exception = std::current_exception(); // Catch all exceptions to be rethrown in parent thread.
}
}}.join();
if (exception)
std::rethrow_exception(exception);
}
void go(unsigned _depth, Executive& _e, OnOpFunc const& _onOp)
{
// If in the offloading point we need to switch to additional separated stack space.
// Current stack is too small to handle more CALL/CREATE executions.
// It needs to be done only once as newly allocated stack space it enough to handle
// the rest of the calls up to the depth limit (c_depthLimit).
if (_depth == c_offloadPoint)
goOnOffloadedStack(_e, _onOp);
else
_e.go(_onOp);
}
}
bool ExtVM::call(CallParameters& _p) bool ExtVM::call(CallParameters& _p)
{ {
Executive e(m_s, lastHashes, depth + 1); Executive e(m_s, lastHashes, depth + 1);
if (!e.call(_p, gasPrice, origin)) if (!e.call(_p, gasPrice, origin))
{ {
e.go(_p.onOp); go(depth, e, _p.onOp);
e.accrueSubState(sub); e.accrueSubState(sub);
} }
_p.gas = e.gas(); _p.gas = e.gas();
@ -47,7 +98,7 @@ h160 ExtVM::create(u256 _endowment, u256& io_gas, bytesConstRef _code, OnOpFunc
Executive e(m_s, lastHashes, depth + 1); Executive e(m_s, lastHashes, depth + 1);
if (!e.create(myAddress, _endowment, gasPrice, io_gas, _code, origin)) if (!e.create(myAddress, _endowment, gasPrice, io_gas, _code, origin))
{ {
e.go(_onOp); go(depth, e, _onOp);
e.accrueSubState(sub); e.accrueSubState(sub);
} }
io_gas = e.gas(); io_gas = e.gas();

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);
} }

1
libethereum/Interface.h

@ -137,6 +137,7 @@ public:
virtual std::pair<h256, unsigned> transactionLocation(h256 const& _transactionHash) const = 0; virtual std::pair<h256, unsigned> transactionLocation(h256 const& _transactionHash) const = 0;
virtual h256 hashFromNumber(BlockNumber _number) const = 0; virtual h256 hashFromNumber(BlockNumber _number) const = 0;
virtual BlockNumber numberFromHash(h256 _blockHash) const = 0; virtual BlockNumber numberFromHash(h256 _blockHash) const = 0;
virtual int compareBlockHashes(h256 _h1, h256 _h2) const = 0;
virtual BlockInfo blockInfo(h256 _hash) const = 0; virtual BlockInfo blockInfo(h256 _hash) const = 0;
virtual BlockDetails blockDetails(h256 _hash) const = 0; virtual BlockDetails blockDetails(h256 _hash) const = 0;

27
libethereum/LogFilter.cpp

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

15
libethereum/LogFilter.h

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

218
libethereum/State.cpp

@ -141,7 +141,12 @@ 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); boost::timer t;
auto vb = BlockChain::verifyBlock(b);
cnote << "verifyBlock:" << t.elapsed();
t.restart();
enact(vb, _bc, _ir);
cnote << "enact:" << t.elapsed();
} }
else else
{ {
@ -393,7 +398,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;
@ -404,8 +409,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();
@ -421,7 +426,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
@ -594,6 +599,7 @@ string State::vmTrace(bytesConstRef _block, BlockChain const& _bc, ImportRequire
for (auto const& tr: rlp[1]) for (auto const& tr: rlp[1])
{ {
StandardTrace st; StandardTrace st;
st.setShowMnemonics();
execute(lh, Transaction(tr.data(), CheckTransaction::Everything), Permanence::Committed, [&](uint64_t _steps, Instruction _inst, bigint _newMemSize, bigint _gasCost, bigint _gas, VM* _vm, ExtVMFace const* _extVM) { st(_steps, _inst, _newMemSize, _gasCost, _gas, _vm, _extVM); }); 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(); ret += (ret.empty() ? "[" : ",") + st.json();
@ -605,15 +611,14 @@ string State::vmTrace(bytesConstRef _block, BlockChain const& _bc, ImportRequire
return ret.empty() ? "[]" : (ret + "]"); 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
@ -622,77 +627,57 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, ImportRequirement
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); try
execute(lh, Transaction(tr.data(), CheckTransaction::Everything)); {
} LogOverride<ExecutiveWarnChannel> o(false);
catch (Exception& ex) execute(lh, tr);
{ }
badBlock(_block, "Invalid transaction: " + toString(toTransactionException(ex))); catch (Exception& ex)
cwarn << " Transaction Index:" << i; {
LogOverride<ExecutiveWarnChannel> o(true); ex << errinfo_transactionIndex(i);
DEV_IGNORE_EXCEPTIONS(execute(lh, Transaction(tr.data(), CheckTransaction::Everything))); throw;
}
ex << errinfo_transactionIndex(i); RLPStream receiptRLP;
throw; 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");
cwarn << " Received: " << toString(m_currentBlock.receiptsRoot);
cwarn << " Expected: " << toString(receiptsRoot) << " which is:";
for (unsigned j = 0; j < i; ++j)
{
auto b = receipts[j];
cwarn << j << ": ";
cwarn << " RLP: " << RLP(b);
cwarn << " Hex: " << toHex(b);
cwarn << " " << TransactionReceipt(&b);
}
cwarn << " VMTrace:\n" << vmTrace(_block, _bc, _ir);
InvalidReceiptsStateRoot ex; InvalidReceiptsStateRoot ex;
ex << Hash256RequirementError(receiptsRoot, m_currentBlock.receiptsRoot); ex << Hash256RequirementError(receiptsRoot, m_currentBlock.receiptsRoot);
ex << errinfo_receipts(receipts); ex << errinfo_receipts(receipts);
ex << errinfo_vmtrace(vmTrace(_block, _bc, _ir)); ex << errinfo_vmtrace(vmTrace(_block.block, _bc, _ir));
BOOST_THROW_EXCEPTION(ex); BOOST_THROW_EXCEPTION(ex);
} }
if (m_currentBlock.logBloom != logBloom()) if (m_currentBlock.logBloom != logBloom())
{ {
badBlock(_block, "Bad log bloom");
cwarn << " Receipt blooms:";
for (unsigned j = 0; j < i; ++j)
{
auto b = receipts[j];
cwarn << " " << j << ":" << TransactionReceipt(&b).bloom().hex();
}
cwarn << " Final bloom:" << m_currentBlock.logBloom.hex();
InvalidLogBloom ex; InvalidLogBloom ex;
ex << LogBloomRequirementError(logBloom(), m_currentBlock.logBloom); ex << LogBloomRequirementError(logBloom(), m_currentBlock.logBloom);
ex << errinfo_receipts(receipts); ex << errinfo_receipts(receipts);
@ -705,82 +690,78 @@ 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() << errinfo_max(2) << errinfo_got(rlp[2].itemCount())); 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());
unsigned ii = 0; unsigned ii = 0;
for (auto const& i: rlp[2]) DEV_TIMED_ABOVE(uncleCheck, 500)
{ for (auto const& i: rlp[2])
try
{ {
auto h = sha3(i.data()); try
if (excluded.count(h))
{ {
badBlock(_block, "Invalid uncle included"); auto h = sha3(i.data());
UncleInChain ex; if (excluded.count(h))
ex << errinfo_comment("Uncle in block already mentioned"); {
ex << errinfo_unclesExcluded(excluded); UncleInChain ex;
ex << errinfo_hash256(sha3(i.data())); ex << errinfo_comment("Uncle in block already mentioned");
BOOST_THROW_EXCEPTION(ex); ex << errinfo_unclesExcluded(excluded);
} ex << errinfo_hash256(sha3(i.data()));
excluded.insert(h); BOOST_THROW_EXCEPTION(ex);
}
excluded.insert(h);
BlockInfo uncle; BlockInfo uncle = BlockInfo::fromHeader(i.data(), (_ir & ImportRequirements::CheckUncles) ? CheckEverything : IgnoreNonce, h);
BlockInfo uncleParent;
uncle = BlockInfo::fromHeader(i.data(), (_ir & ImportRequirements::CheckUncles) ? CheckEverything : IgnoreNonce, h);
if (!_bc.isKnown(uncle.parentHash))
BOOST_THROW_EXCEPTION(UnknownParent());
uncleParent = BlockInfo(_bc.block(uncle.parentHash)); BlockInfo uncleParent;
if ((bigint)uncleParent.number < (bigint)m_currentBlock.number - 7) if (!_bc.isKnown(uncle.parentHash))
{ BOOST_THROW_EXCEPTION(UnknownParent());
badBlock(_block, "Uncle too old"); uncleParent = BlockInfo(_bc.block(uncle.parentHash));
cwarn << " Uncle number: " << uncle.number;
cwarn << " Uncle parent number: " << uncleParent.number; if ((bigint)uncleParent.number < (bigint)m_currentBlock.number - 7)
cwarn << " Block number: " << m_currentBlock.number; {
UncleTooOld ex; UncleTooOld ex;
ex << errinfo_uncleNumber(uncle.number); ex << errinfo_uncleNumber(uncle.number);
ex << errinfo_currentNumber(m_currentBlock.number); ex << errinfo_currentNumber(m_currentBlock.number);
BOOST_THROW_EXCEPTION(ex); 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;
} }
else if (uncle.number == m_currentBlock.number) catch (Exception& ex)
{ {
badBlock(_block, "Uncle is brother"); ex << errinfo_uncleIndex(ii);
cwarn << " Uncle number: " << uncle.number; throw;
cwarn << " Uncle parent number: " << uncleParent.number;
cwarn << " Block number: " << m_currentBlock.number;
UncleIsBrother ex;
ex << errinfo_uncleNumber(uncle.number);
ex << errinfo_currentNumber(m_currentBlock.number);
BOOST_THROW_EXCEPTION(ex);
} }
uncle.verifyParent(uncleParent);
// tdIncrease += uncle.difficulty;
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");
m_db.rollback(); m_db.rollback();
BOOST_THROW_EXCEPTION(InvalidStateRoot() << Hash256RequirementError(rootHash(), m_currentBlock.stateRoot)); BOOST_THROW_EXCEPTION(InvalidStateRoot() << Hash256RequirementError(rootHash(), m_currentBlock.stateRoot));
} }
@ -788,7 +769,6 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, ImportRequirement
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)));
} }
@ -803,7 +783,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);
@ -816,7 +797,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;
@ -866,7 +848,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;
} }
@ -1165,6 +1147,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();
} }
@ -1203,6 +1187,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

5
libethereum/State.h

@ -64,6 +64,7 @@ using LogBloomRequirementError = boost::tuple<errinfo_required_LogBloom, errinfo
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; };
@ -326,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
@ -367,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);

21
libethereum/Transaction.cpp

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

3
libethereum/Transaction.h

@ -228,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.

14
libevm/VMFace.h

@ -25,14 +25,14 @@ namespace dev
namespace eth namespace eth
{ {
#define ETH_SIMPLE_EXCEPTION_VM(X) struct X: virtual VMException { public X(): VMException(#X) {} };
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

69
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;
} }
@ -74,31 +79,43 @@ void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item, bool _co
void CommonSubexpressionEliminator::optimizeBreakingItem() void CommonSubexpressionEliminator::optimizeBreakingItem()
{ {
if (!m_breakingItem || *m_breakingItem != AssemblyItem(Instruction::JUMPI)) if (!m_breakingItem)
return; return;
ExpressionClasses& classes = m_state.expressionClasses();
SourceLocation const& location = m_breakingItem->getLocation(); SourceLocation const& location = m_breakingItem->getLocation();
AssemblyItem::JumpType jumpType = m_breakingItem->getJumpType(); if (*m_breakingItem == AssemblyItem(Instruction::JUMPI))
Id condition = m_state.stackElement(m_state.stackHeight() - 1, location);
Id zero = m_state.expressionClasses().find(u256(0));
if (m_state.expressionClasses().knownToBeDifferent(condition, zero))
{ {
feedItem(AssemblyItem(Instruction::SWAP1, location), true); AssemblyItem::JumpType jumpType = m_breakingItem->getJumpType();
feedItem(AssemblyItem(Instruction::POP, location), true);
AssemblyItem item(Instruction::JUMP, location); Id condition = m_state.stackElement(m_state.stackHeight() - 1, location);
item.setJumpType(jumpType); if (classes.knownNonZero(condition))
m_breakingItem = m_state.expressionClasses().storeItem(item); {
return; feedItem(AssemblyItem(Instruction::SWAP1, location), true);
feedItem(AssemblyItem(Instruction::POP, location), true);
AssemblyItem item(Instruction::JUMP, location);
item.setJumpType(jumpType);
m_breakingItem = classes.storeItem(item);
}
else if (classes.knownZero(condition))
{
AssemblyItem it(Instruction::POP, location);
feedItem(it, true);
feedItem(it, true);
m_breakingItem = nullptr;
}
} }
Id negatedCondition = m_state.expressionClasses().find(Instruction::ISZERO, {condition}); else if (*m_breakingItem == AssemblyItem(Instruction::RETURN))
if (m_state.expressionClasses().knownToBeDifferent(negatedCondition, zero))
{ {
AssemblyItem it(Instruction::POP, location); Id size = m_state.stackElement(m_state.stackHeight() - 1, location);
feedItem(it, true); if (classes.knownZero(size))
feedItem(it, true); {
m_breakingItem = nullptr; feedItem(AssemblyItem(Instruction::POP, location), true);
feedItem(AssemblyItem(Instruction::POP, location), true);
AssemblyItem item(Instruction::STOP, location);
m_breakingItem = classes.storeItem(item);
}
} }
} }

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",

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

File diff suppressed because it is too large

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

File diff suppressed because one or more lines are too long

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

File diff suppressed because one or more lines are too long

1940
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) {

27
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 desc = [{ var compiled = web3.eth.compile.solidity(source);
"constant" : false, var code = compiled.Contract.code;
"inputs" : [], var abi = compiled.Contract.info.abiDefinition;
"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/gulpfile.js

@ -73,7 +73,7 @@ gulp.task('buildLight', ['clean'], function () {
.pipe(gulp.dest( DEST )); .pipe(gulp.dest( DEST ));
}); });
gulp.task('buildStandalone', ['clean'], function () { gulp.task('buildStandalone', [], function () {
return browserify(browserifyOptions) return browserify(browserifyOptions)
.require('./' + src + '.js', {expose: 'web3'}) .require('./' + src + '.js', {expose: 'web3'})
.require('bignumber.js') // expose it to dapp users .require('bignumber.js') // expose it to dapp users

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;

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

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

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

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

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

@ -36,22 +36,30 @@
var BigNumber = require('bignumber.js'); var 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"
} }

26
libjsqrc/ethereumjs/lib/web3.js

@ -34,18 +34,11 @@ var Filter = require('./web3/filter');
var utils = require('./utils/utils'); 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 Method = require('./web3/method'); var Method = require('./web3/method');
var c = require('./utils/config');
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 +123,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 +151,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);
@ -165,5 +159,17 @@ setupProperties(web3.eth, eth.properties);
setupMethods(web3.db, db.methods); setupMethods(web3.db, db.methods);
setupMethods(web3.shh, shh.methods); setupMethods(web3.shh, shh.methods);
web3.admin = {};
web3.admin.setSessionKey = function(s) { web3.admin.sessionKey = s; };
var blockQueueStatus = new Property({
name: 'blockQueueStatus',
call: 'admin_eth_blockQueueStatus',
params: 1,
inputFormatter: [function() { return web3.admin.sessionKey; }]
});
setupMethods(web3.admin, [blockQueueStatus]);
module.exports = web3; module.exports = web3;

10
libjsqrc/ethereumjs/lib/web3/eth.js

@ -77,11 +77,11 @@ var uncleCountCall = function (args) {
/// @returns an array of objects describing web3.eth api methods /// @returns an array of objects describing web3.eth api methods
var getBalance = new Method({ var getBalance = new Method({
name: 'getBalance', name: 'getBalance',
call: 'eth_getBalance', call: 'eth_getBalance',
params: 2, params: 2,
inputFormatter: [utils.toAddress, formatters.inputDefaultBlockNumberFormatter], inputFormatter: [utils.toAddress, formatters.inputDefaultBlockNumberFormatter],
outputFormatter: formatters.outputBigNumberFormatter outputFormatter: formatters.outputBigNumberFormatter
}); });
var getStorageAt = new Method({ var getStorageAt = new Method({

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);

1
libjsqrc/ethereumjs/lib/web3/property.js

@ -29,6 +29,7 @@ var Property = function (options) {
this.setter = options.setter; this.setter = options.setter;
this.outputFormatter = options.outputFormatter; this.outputFormatter = options.outputFormatter;
this.inputFormatter = options.inputFormatter; this.inputFormatter = options.inputFormatter;
this.params = options.params;
}; };
/** /**

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

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

2
libjsqrc/ethereumjs/package.js

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

3
libjsqrc/ethereumjs/package.json

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

4
libjsqrc/ethereumjs/test/batch.js

@ -58,10 +58,6 @@ describe('lib/web3/batch', function () {
var address = '0x0000000000000000000000000000000000000000'; var address = '0x0000000000000000000000000000000000000000';
var result = '0x126'; var result = '0x126';
var result2 = '0x0000000000000000000000000000000000000000000000000000000000000123'; var result2 = '0x0000000000000000000000000000000000000000000000000000000000000123';
var signature = '0x001122334455';
// TODO: fix this, maybe in browser sha3?
provider.injectResult(signature);
var counter = 0; var counter = 0;
var callback = function (err, r) { var callback = function (err, r) {

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

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

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

Loading…
Cancel
Save