Browse Source

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

cl-refactor
Paweł Bylica 10 years ago
parent
commit
f0b03ed8ce
  1. 66
      CMakeLists.txt
  2. 12
      alethzero/Main.ui
  3. 21
      alethzero/MainWin.cpp
  4. 1
      alethzero/MainWin.h
  5. 3
      alethzero/OurWebThreeStubServer.cpp
  6. 2
      alethzero/Transact.cpp
  7. 2
      cmake/FindOpenCL.cmake
  8. 578
      eth/main.cpp
  9. 7
      ethminer/CMakeLists.txt
  10. 488
      ethminer/MinerAux.h
  11. 417
      ethminer/main.cpp
  12. 2
      exp/main.cpp
  13. 2
      libdevcore/Common.cpp
  14. 45
      libdevcore/CommonIO.cpp
  15. 1
      libdevcore/Exceptions.h
  16. 21
      libdevcrypto/FileSystem.cpp
  17. 29
      libdevcrypto/OverlayDB.cpp
  18. 93
      libdevcrypto/TrieDB.h
  19. 33
      libdevcrypto/TrieHash.cpp
  20. 15
      libdevcrypto/TrieHash.h
  21. 5
      libethash-cl/ethash_cl_miner.cpp
  22. 1
      libethash-cl/ethash_cl_miner.h
  23. 4
      libethash/CMakeLists.txt
  24. 9
      libethash/io_posix.c
  25. 4
      libethash/io_win32.c
  26. 39
      libethcore/BlockInfo.cpp
  27. 2
      libethcore/CMakeLists.txt
  28. 6
      libethcore/Common.cpp
  29. 5
      libethcore/Common.h
  30. 31
      libethcore/Ethash.cpp
  31. 6
      libethcore/Ethash.h
  32. 82
      libethcore/EthashAux.cpp
  33. 21
      libethcore/EthashAux.h
  34. 0
      libethcore/Farm.h
  35. 139
      libethereum/BlockChain.cpp
  36. 9
      libethereum/BlockChain.h
  37. 158
      libethereum/BlockQueue.cpp
  38. 35
      libethereum/BlockQueue.h
  39. 21
      libethereum/Client.cpp
  40. 2
      libethereum/Client.h
  41. 2
      libethereum/ClientBase.cpp
  42. 5
      libethereum/EthereumHost.cpp
  43. 2
      libethereum/EthereumHost.h
  44. 13
      libethereum/EthereumPeer.cpp
  45. 4
      libethereum/EthereumPeer.h
  46. 3
      libethereum/Executive.cpp
  47. 2
      libethereum/Executive.h
  48. 0
      libethereum/Farm.cpp
  49. 67
      libethereum/State.cpp
  50. 2
      libethereum/State.h
  51. 147
      libethereum/TransactionQueue.cpp
  52. 14
      libethereum/TransactionQueue.h
  53. 16
      libevmasm/Assembly.cpp
  54. 7
      libevmasm/CommonSubexpressionEliminator.cpp
  55. 51
      libevmasm/ControlFlowGraph.cpp
  56. 4
      libevmasm/ControlFlowGraph.h
  57. 10
      libevmasm/ExpressionClasses.cpp
  58. 6
      libevmasm/ExpressionClasses.h
  59. 92
      libevmasm/KnownState.cpp
  60. 19
      libevmasm/KnownState.h
  61. 5
      libjsconsole/JSConsole.cpp
  62. 4
      libjsconsole/JSConsole.h
  63. 2
      libjsqrc/ethereumjs/bower.json
  64. 591
      libjsqrc/ethereumjs/dist/web3-light.js
  65. 30
      libjsqrc/ethereumjs/dist/web3-light.js.map
  66. 4
      libjsqrc/ethereumjs/dist/web3-light.min.js
  67. 593
      libjsqrc/ethereumjs/dist/web3.js
  68. 30
      libjsqrc/ethereumjs/dist/web3.js.map
  69. 4
      libjsqrc/ethereumjs/dist/web3.min.js
  70. 44
      libjsqrc/ethereumjs/example/contract.html
  71. 64
      libjsqrc/ethereumjs/example/event_inc.html
  72. 4
      libjsqrc/ethereumjs/example/node-app.js
  73. 1
      libjsqrc/ethereumjs/index.js
  74. 44
      libjsqrc/ethereumjs/lib/solidity/abi.js
  75. 45
      libjsqrc/ethereumjs/lib/solidity/utils.js
  76. 1
      libjsqrc/ethereumjs/lib/utils/utils.js
  77. 2
      libjsqrc/ethereumjs/lib/version.json
  78. 4
      libjsqrc/ethereumjs/lib/web3.js
  79. 61
      libjsqrc/ethereumjs/lib/web3/batch.js
  80. 166
      libjsqrc/ethereumjs/lib/web3/contract.js
  81. 23
      libjsqrc/ethereumjs/lib/web3/eth.js
  82. 89
      libjsqrc/ethereumjs/lib/web3/function.js
  83. 17
      libjsqrc/ethereumjs/lib/web3/method.js
  84. 40
      libjsqrc/ethereumjs/lib/web3/property.js
  85. 27
      libjsqrc/ethereumjs/lib/web3/requestmanager.js
  86. 15
      libjsqrc/ethereumjs/lib/web3/watches.js
  87. 2
      libjsqrc/ethereumjs/package.js
  88. 2
      libjsqrc/ethereumjs/package.json
  89. 106
      libjsqrc/ethereumjs/test/abi.formatConstructorParams.js
  90. 69
      libjsqrc/ethereumjs/test/async.js
  91. 86
      libjsqrc/ethereumjs/test/batch.js
  92. 100
      libjsqrc/ethereumjs/test/contract.js
  93. 23
      libjsqrc/ethereumjs/test/method.request.js
  94. 2
      libjsqrc/ethereumjs/test/node/app.js
  95. 2
      libjsqrc/ethereumjs/test/node/package.json
  96. 21
      libjsqrc/ethereumjs/test/polling.js
  97. 20
      libjsqrc/ethereumjs/test/web3.eth.blockNumber.js
  98. 41
      libjsqrc/ethereumjs/test/web3.eth.call.js
  99. 21
      libjsqrc/ethereumjs/test/web3.eth.contract.js
  100. 25
      libjsqrc/ethereumjs/test/web3.eth.estimateGas.js

66
CMakeLists.txt

@ -32,6 +32,7 @@ option(USENPM "Use npm to recompile ethereum.js if it was changed" OFF)
option(PROFILING "Build in support for profiling" OFF) option(PROFILING "Build in support for profiling" OFF)
set(BUNDLE "none" CACHE STRING "Predefined bundle of software to build (none, full, user, tests, minimal).") set(BUNDLE "none" CACHE STRING "Predefined bundle of software to build (none, full, user, tests, minimal).")
option(MINER "Build the miner 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)
@ -187,6 +188,7 @@ eth_format_option(VMTRACE)
eth_format_option(EVMJIT) eth_format_option(EVMJIT)
eth_format_option(FATDB) eth_format_option(FATDB)
eth_format_option(JSONRPC) eth_format_option(JSONRPC)
eth_format_option(MINER)
eth_format_option(USENPM) eth_format_option(USENPM)
eth_format_option(PROFILING) eth_format_option(PROFILING)
eth_format_option(SOLIDITY) eth_format_option(SOLIDITY)
@ -249,6 +251,16 @@ elseif (BUNDLE STREQUAL "user")
set(NCURSES ${DECENT_PLATFORM}) set(NCURSES ${DECENT_PLATFORM})
set(TOOLS ON) set(TOOLS ON)
set(TESTS OFF) set(TESTS OFF)
elseif (BUNDLE STREQUAL "miner")
set(SERPENT OFF)
set(SOLIDITY OFF)
set(USENPM OFF)
set(GUI OFF)
set(NCURSES OFF)
set(TOOLS OFF)
set(TESTS OFF)
set(MINER ON)
set(ETHASHCL ON)
endif () endif ()
# Default CMAKE_BUILD_TYPE to "Release". # Default CMAKE_BUILD_TYPE to "Release".
@ -283,6 +295,7 @@ message("-- FATDB Full database exploring ${FATDB}")
message("-- JSONRPC JSON-RPC support ${JSONRPC}") message("-- JSONRPC JSON-RPC support ${JSONRPC}")
message("-- USENPM Javascript source building ${USENPM}") message("-- USENPM Javascript source building ${USENPM}")
message("------------------------------------------------------------- components") message("------------------------------------------------------------- components")
message("-- MINER Build miner ${MINER}")
message("-- TOOLS Build basic tools ${TOOLS}") message("-- TOOLS Build basic tools ${TOOLS}")
message("-- SOLIDITY Build Solidity language components ${SOLIDITY}") message("-- SOLIDITY Build Solidity language components ${SOLIDITY}")
message("-- SERPENT Build Serpent language components ${SERPENT}") message("-- SERPENT Build Serpent language components ${SERPENT}")
@ -310,10 +323,20 @@ if (EVMJIT)
add_subdirectory(evmjit) add_subdirectory(evmjit)
endif() endif()
if (TOOLS OR GUI OR SOLIDITY OR NCURSES OR TESTS)
set(GENERAL 1)
else ()
set(GENERAL 0)
endif ()
message("GENERAL ${GENERAL}")
add_subdirectory(libdevcore) add_subdirectory(libdevcore)
add_subdirectory(libevmcore) if (GENERAL)
add_subdirectory(libevmasm) add_subdirectory(libevmcore)
add_subdirectory(liblll) add_subdirectory(libevmasm)
add_subdirectory(liblll)
endif ()
if (SERPENT) if (SERPENT)
add_subdirectory(libserpent) add_subdirectory(libserpent)
@ -329,31 +352,43 @@ if (TOOLS)
if (SOLIDITY) if (SOLIDITY)
add_subdirectory(solc) add_subdirectory(solc)
endif () endif ()
endif() endif ()
if (JSONRPC) if (JSONRPC AND GENERAL)
add_subdirectory(libweb3jsonrpc) add_subdirectory(libweb3jsonrpc)
endif() endif ()
if (JSCONSOLE) if (JSCONSOLE)
add_subdirectory(libjsengine) add_subdirectory(libjsengine)
add_subdirectory(libjsconsole) add_subdirectory(libjsconsole)
endif() endif ()
add_subdirectory(secp256k1) add_subdirectory(secp256k1)
add_subdirectory(libp2p)
add_subdirectory(libdevcrypto) add_subdirectory(libdevcrypto)
add_subdirectory(libwhisper)
add_subdirectory(libethash) if (GENERAL)
if (ETHASHCL) add_subdirectory(libp2p)
add_subdirectory(libethash-cl) add_subdirectory(libwhisper)
endif ()
if (GENERAL OR MINER)
add_subdirectory(libethash)
if (ETHASHCL)
add_subdirectory(libethash-cl)
endif ()
endif () endif ()
add_subdirectory(libethcore) add_subdirectory(libethcore)
add_subdirectory(libevm)
add_subdirectory(libethereum) if (GENERAL)
add_subdirectory(libwebthree) add_subdirectory(libevm)
add_subdirectory(libethereum)
add_subdirectory(libwebthree)
endif ()
if (MINER)
add_subdirectory(ethminer)
endif ()
if (TESTS) if (TESTS)
add_subdirectory(libtestutils) add_subdirectory(libtestutils)
@ -367,7 +402,6 @@ if (TOOLS)
add_subdirectory(rlp) add_subdirectory(rlp)
add_subdirectory(abi) add_subdirectory(abi)
add_subdirectory(ethminer)
add_subdirectory(eth) add_subdirectory(eth)
if("x${CMAKE_BUILD_TYPE}" STREQUAL "xDebug") if("x${CMAKE_BUILD_TYPE}" STREQUAL "xDebug")

12
alethzero/Main.ui

@ -185,6 +185,7 @@
<addaction name="usePrivate"/> <addaction name="usePrivate"/>
<addaction name="jitvm"/> <addaction name="jitvm"/>
<addaction name="retryUnknown"/> <addaction name="retryUnknown"/>
<addaction name="confirm"/>
</widget> </widget>
<widget class="QMenu" name="menu_View"> <widget class="QMenu" name="menu_View">
<property name="title"> <property name="title">
@ -1733,6 +1734,17 @@ font-size: 14pt</string>
<string>Prepare Next &amp;DAG</string> <string>Prepare Next &amp;DAG</string>
</property> </property>
</action> </action>
<action name="confirm">
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="text">
<string>Co&amp;nfirm Transactions</string>
</property>
</action>
</widget> </widget>
<layoutdefault spacing="6" margin="11"/> <layoutdefault spacing="6" margin="11"/>
<customwidgets> <customwidgets>

21
alethzero/MainWin.cpp

@ -212,7 +212,7 @@ Main::Main(QWidget *parent) :
m_server->setIdentities(keysAsVector(owned())); m_server->setIdentities(keysAsVector(owned()));
m_server->StartListening(); m_server->StartListening();
WebPage* webPage= new WebPage(this); WebPage* webPage = new WebPage(this);
m_webPage = webPage; m_webPage = webPage;
connect(webPage, &WebPage::consoleMessage, [this](QString const& _msg) { Main::addConsoleMessage(_msg, QString()); }); connect(webPage, &WebPage::consoleMessage, [this](QString const& _msg) { Main::addConsoleMessage(_msg, QString()); });
ui->webView->setPage(m_webPage); ui->webView->setPage(m_webPage);
@ -368,6 +368,11 @@ Address Main::getCurrencies() const
return abiOut<Address>(ethereum()->call(c_newConfig, abiIn("lookup(uint256)", (u256)3)).output); return abiOut<Address>(ethereum()->call(c_newConfig, abiIn("lookup(uint256)", (u256)3)).output);
} }
bool Main::doConfirm()
{
return ui->confirm->isChecked();
}
void Main::installNameRegWatch() void Main::installNameRegWatch()
{ {
uninstallWatch(m_nameRegFilter); uninstallWatch(m_nameRegFilter);
@ -945,7 +950,11 @@ void Main::on_preview_triggered()
void Main::on_prepNextDAG_triggered() void Main::on_prepNextDAG_triggered()
{ {
EthashAux::computeFull(ethereum()->blockChain().number() + ETHASH_EPOCH_LENGTH); EthashAux::computeFull(
EthashAux::seedHash(
ethereum()->blockChain().number() + ETHASH_EPOCH_LENGTH
)
);
} }
void Main::refreshMining() void Main::refreshMining()
@ -1010,7 +1019,7 @@ void Main::refreshBalances()
u256 b = ethereum()->balanceAt(i.first); u256 b = ethereum()->balanceAt(i.first);
QListWidgetItem* li = new QListWidgetItem(QString("%4 %2: %1 [%3]").arg(formatBalance(b).c_str()).arg(QString::fromStdString(render(i.first))).arg((unsigned)ethereum()->countAt(i.first)).arg(QString::fromStdString(i.second.first)), ui->ourAccounts); QListWidgetItem* li = new QListWidgetItem(QString("%4 %2: %1 [%3]").arg(formatBalance(b).c_str()).arg(QString::fromStdString(render(i.first))).arg((unsigned)ethereum()->countAt(i.first)).arg(QString::fromStdString(i.second.first)), ui->ourAccounts);
li->setData(Qt::UserRole, QByteArray((char const*)i.first.data(), Address::size)); li->setData(Qt::UserRole, QByteArray((char const*)i.first.data(), Address::size));
li->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled); li->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable);
li->setCheckState(m_beneficiary == i.first ? Qt::Checked : Qt::Unchecked); li->setCheckState(m_beneficiary == i.first ? Qt::Checked : Qt::Unchecked);
totalBalance += b; totalBalance += b;
@ -1147,7 +1156,7 @@ void Main::refreshBlockCount()
{ {
auto d = ethereum()->blockChain().details(); auto d = ethereum()->blockChain().details();
BlockQueueStatus b = ethereum()->blockQueueStatus(); BlockQueueStatus b = ethereum()->blockQueueStatus();
ui->chainStatus->setText(QString("%3 ready %4 future %5 unknown %6 bad %1 #%2").arg(m_privateChain.size() ? "[" + m_privateChain + "] " : "testnet").arg(d.number).arg(b.ready).arg(b.future).arg(b.unknown).arg(b.bad)); ui->chainStatus->setText(QString("%3 ready %4 verifying %5 unverified %6 future %7 unknown %8 bad %1 #%2").arg(m_privateChain.size() ? "[" + m_privateChain + "] " : "testnet").arg(d.number).arg(b.verified).arg(b.verifying).arg(b.unverified).arg(b.future).arg(b.unknown).arg(b.bad));
} }
void Main::on_turboMining_triggered() void Main::on_turboMining_triggered()
@ -1157,6 +1166,9 @@ void Main::on_turboMining_triggered()
void Main::refreshBlockChain() void Main::refreshBlockChain()
{ {
if (!ui->blocks->isVisible())
return;
DEV_TIMED_FUNCTION; DEV_TIMED_FUNCTION;
cwatch << "refreshBlockChain()"; cwatch << "refreshBlockChain()";
@ -1990,6 +2002,7 @@ void Main::on_killAccount_triggered()
m_keyManager.kill(h); m_keyManager.kill(h);
if (m_keyManager.accounts().empty()) if (m_keyManager.accounts().empty())
m_keyManager.import(Secret::random(), "Default account"); m_keyManager.import(Secret::random(), "Default account");
m_beneficiary = *m_keyManager.accounts().begin();
keysChanged(); keysChanged();
if (m_beneficiary == h) if (m_beneficiary == h)
setBeneficiary(*m_keyManager.accounts().begin()); setBeneficiary(*m_keyManager.accounts().begin());

1
alethzero/MainWin.h

@ -94,6 +94,7 @@ public:
dev::u256 gasPrice() const { return 10 * dev::eth::szabo; } dev::u256 gasPrice() const { return 10 * dev::eth::szabo; }
dev::eth::KeyManager& keyManager() override { return m_keyManager; } dev::eth::KeyManager& keyManager() override { return m_keyManager; }
bool doConfirm();
dev::Secret retrieveSecret(dev::Address const& _a) const override; dev::Secret retrieveSecret(dev::Address const& _a) const override;

3
alethzero/OurWebThreeStubServer.cpp

@ -146,6 +146,9 @@ AddressHash OurAccountHolder::realAccounts() const
bool OurAccountHolder::validateTransaction(TransactionSkeleton const& _t, bool _toProxy) bool OurAccountHolder::validateTransaction(TransactionSkeleton const& _t, bool _toProxy)
{ {
if (!m_main->doConfirm())
return true;
if (_t.creation) if (_t.creation)
{ {
// show notice concerning the creation code. TODO: this needs entering into natspec. // show notice concerning the creation code. TODO: this needs entering into natspec.

2
alethzero/Transact.cpp

@ -361,7 +361,7 @@ void Transact::rejigData()
to = m_context->fromString(ui->destination->currentText().toStdString()).first; to = m_context->fromString(ui->destination->currentText().toStdString()).first;
er = ethereum()->call(s, value(), to, m_data, gasNeeded, gasPrice()); er = ethereum()->call(s, value(), to, m_data, gasNeeded, gasPrice());
} }
gasNeeded = (qint64)(er.gasUsed + er.gasRefunded); gasNeeded = (qint64)(er.gasUsed + er.gasRefunded + c_callStipend);
htmlInfo = QString("<div class=\"info\"><span class=\"icon\">INFO</span> Gas required: %1 total = %2 base, %3 exec [%4 refunded later]</div>").arg(gasNeeded).arg(baseGas).arg(gasNeeded - baseGas).arg((qint64)er.gasRefunded) + htmlInfo; htmlInfo = QString("<div class=\"info\"><span class=\"icon\">INFO</span> Gas required: %1 total = %2 base, %3 exec [%4 refunded later]</div>").arg(gasNeeded).arg(baseGas).arg(gasNeeded - baseGas).arg((qint64)er.gasRefunded) + htmlInfo;
if (er.excepted != TransactionException::None) if (er.excepted != TransactionException::None)

2
cmake/FindOpenCL.cmake

@ -118,7 +118,7 @@ if(WIN32)
endif() endif()
else() else()
find_library(OpenCL_LIBRARY find_library(OpenCL_LIBRARY
NAMES OpenCL) NAMES OpenCL libOpenCL.so.1)
endif() endif()
set(OpenCL_LIBRARIES ${OpenCL_LIBRARY}) set(OpenCL_LIBRARIES ${OpenCL_LIBRARY})

578
eth/main.cpp

@ -57,6 +57,7 @@
#include "PhoneHome.h" #include "PhoneHome.h"
#include "Farm.h" #include "Farm.h"
#endif #endif
#include <ethminer/MinerAux.h>
using namespace std; using namespace std;
using namespace dev; using namespace dev;
using namespace dev::p2p; using namespace dev::p2p;
@ -64,18 +65,6 @@ using namespace dev::eth;
using namespace boost::algorithm; using namespace boost::algorithm;
using dev::eth::Instruction; using dev::eth::Instruction;
#undef RETURN
bool isTrue(std::string const& _m)
{
return _m == "on" || _m == "yes" || _m == "true" || _m == "1";
}
bool isFalse(std::string const& _m)
{
return _m == "off" || _m == "no" || _m == "false" || _m == "0";
}
void interactiveHelp() void interactiveHelp()
{ {
cout cout
@ -93,6 +82,7 @@ void interactiveHelp()
<< " mineforce <enable> Forces mining, even when there are no transactions." << endl << " mineforce <enable> Forces mining, even when there are no transactions." << endl
<< " block Gives the current block height." << endl << " block Gives the current block height." << endl
<< " 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
<< " transact Execute a given transaction." << endl << " transact Execute a given 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
@ -158,33 +148,13 @@ void help()
<< " --port <port> Connect to remote port (default: 30303)." << endl << " --port <port> Connect to remote port (default: 30303)." << endl
<< " --network-id <n> Only connect to other hosts with this network id (default:0)." << endl << " --network-id <n> Only connect to other hosts with this network id (default:0)." << endl
<< " --upnp <on/off> Use UPnP for NAT (default: on)." << endl << " --upnp <on/off> Use UPnP for NAT (default: on)." << endl
<< endl << endl;
MinerCLI::streamHelp(cout);
cout
<< "Client structured logging:" << endl << "Client structured logging:" << endl
<< " --structured-logging Enable structured logging (default output to stdout)." << endl << " --structured-logging Enable structured logging (default output to stdout)." << endl
<< " --structured-logging-format <format> Set the structured logging time format." << endl << " --structured-logging-format <format> Set the structured logging time format." << endl
<< " --structured-logging-url <URL> Set the structured logging destination (currently only file:// supported)." << endl << " --structured-logging-url <URL> Set the structured logging destination (currently only file:// supported)." << endl
#if ETH_JSONRPC || !ETH_TRUE
<< endl
<< "Work farming mode:" << endl
<< " -F,--farm <url> Put into mining farm mode with the work server at URL. Use with -G/--opencl." << endl
<< " --farm-recheck <n> Leave n ms between checks for changed work (default: 500)." << endl
#endif
<< endl
<< "Ethash verify mode:" << endl
<< " -w,--check-pow <headerHash> <seedHash> <difficulty> <nonce> Check PoW credentials for validity." << endl
<< endl
<< "Benchmarking mode:" << endl
<< " -M,--benchmark Benchmark for mining and exit; use with --cpu and --opencl." << endl
<< " --benchmark-warmup <seconds> Set the duration of warmup for the benchmark tests (default: 3)." << endl
<< " --benchmark-trial <seconds> Set the duration for each trial for the benchmark tests (default: 3)." << endl
<< " --benchmark-trials <n> Set the duration of warmup for the benchmark tests (default: 5)." << endl
#if ETH_JSONRPC || !ETH_TRUE
<< " --phone-home <on/off> When benchmarking, publish results (default: on)" << endl
#endif
<< endl
<< "DAG creation mode:" << endl
<< " -D,--create-dag <this/next/number> Create the DAG in preparation for mining on given block and exit." << endl
<< endl
<< "Import/export modes:" << endl << "Import/export modes:" << endl
<< " -I,--import <file> Import file as a concatenated series of blocks and exit." << endl << " -I,--import <file> Import file as a concatenated series of blocks and exit." << endl
<< " -E,--export <file> Export file as a concatenated series of blocks and exit." << endl << " -E,--export <file> Export file as a concatenated series of blocks and exit." << endl
@ -207,20 +177,13 @@ void help()
exit(0); exit(0);
} }
string credits(bool _interactive = false) string ethCredits(bool _interactive = false)
{ {
std::ostringstream cout; std::ostringstream cout;
cout
<< "Ethereum (++) " << dev::Version << endl
<< " Code by Gav Wood et al, (c) 2013, 2014, 2015." << endl
<< " Based on a design by Vitalik Buterin." << endl << endl;
if (_interactive) if (_interactive)
cout cout
<< "Type 'netstart 30303' to start networking" << endl
<< "Type 'connect " << Host::pocHost() << " 30303' to connect" << endl
<< "Type 'exit' to quit" << endl << endl; << "Type 'exit' to quit" << endl << endl;
return cout.str(); return credits() + cout.str();
} }
void version() void version()
@ -262,23 +225,11 @@ enum class NodeMode
Full Full
}; };
void doInitDAG(unsigned _n)
{
BlockInfo bi;
bi.number = _n;
cout << "Initializing DAG for epoch beginning #" << (bi.number / 30000 * 30000) << " (seedhash " << bi.seedHash().abridged() << "). This will take a while." << endl;
Ethash::prep(bi);
exit(0);
}
enum class OperationMode enum class OperationMode
{ {
Node, Node,
Import, Import,
Export, Export
DAGInit,
Benchmark,
Farm
}; };
enum class Format enum class Format
@ -288,148 +239,6 @@ enum class Format
Human Human
}; };
enum class MinerType
{
CPU,
GPU
};
void doBenchmark(MinerType _m, bool _phoneHome, unsigned _warmupDuration = 15, unsigned _trialDuration = 3, unsigned _trials = 5)
{
BlockInfo genesis = CanonBlockChain::genesis();
genesis.difficulty = 1 << 18;
cdebug << genesis.boundary();
GenericFarm<Ethash> f;
f.onSolutionFound([&](ProofOfWork::Solution) { return false; });
string platformInfo = _m == MinerType::CPU ? ProofOfWork::CPUMiner::platformInfo() : _m == MinerType::GPU ? ProofOfWork::GPUMiner::platformInfo() : "";
cout << "Benchmarking on platform: " << platformInfo << endl;
cout << "Preparing DAG..." << endl;
Ethash::prep(genesis);
genesis.difficulty = u256(1) << 63;
genesis.noteDirty();
f.setWork(genesis);
if (_m == MinerType::CPU)
f.startCPU();
else if (_m == MinerType::GPU)
f.startGPU();
map<uint64_t, MiningProgress> results;
uint64_t mean = 0;
uint64_t innerMean = 0;
for (unsigned i = 0; i <= _trials; ++i)
{
if (!i)
cout << "Warming up..." << endl;
else
cout << "Trial " << i << "... " << flush;
this_thread::sleep_for(chrono::seconds(i ? _trialDuration : _warmupDuration));
auto mp = f.miningProgress();
f.resetMiningProgress();
if (!i)
continue;
auto rate = mp.rate();
cout << rate << endl;
results[rate] = mp;
mean += rate;
if (i > 1 && i < 5)
innerMean += rate;
}
f.stop();
innerMean /= (_trials - 2);
cout << "min/mean/max: " << results.begin()->second.rate() << "/" << (mean / _trials) << "/" << results.rbegin()->second.rate() << " H/s" << endl;
cout << "inner mean: " << innerMean << " H/s" << endl;
(void)_phoneHome;
#if ETH_JSONRPC || !ETH_TRUE
if (_phoneHome)
{
cout << "Phoning home to find world ranking..." << endl;
jsonrpc::HttpClient client("http://gav.ethdev.com:3000/benchmark");
PhoneHome rpc(client);
try
{
unsigned ranking = rpc.report_benchmark(platformInfo, innerMean);
cout << "Ranked: " << ranking << " of all benchmarks." << endl;
}
catch (...)
{
cout << "Error phoning home. ET is sad." << endl;
}
}
#endif
exit(0);
}
struct HappyChannel: public LogChannel { static const char* name() { return ":-D"; } static const int verbosity = 1; };
struct SadChannel: public LogChannel { static const char* name() { return ":-("; } static const int verbosity = 1; };
void doFarm(MinerType _m, string const& _remote, unsigned _recheckPeriod)
{
(void)_m;
(void)_remote;
(void)_recheckPeriod;
#if ETH_JSONRPC || !ETH_TRUE
jsonrpc::HttpClient client(_remote);
Farm rpc(client);
GenericFarm<Ethash> f;
if (_m == MinerType::CPU)
f.startCPU();
else if (_m == MinerType::GPU)
f.startGPU();
ProofOfWork::WorkPackage current;
while (true)
try
{
bool completed = false;
ProofOfWork::Solution solution;
f.onSolutionFound([&](ProofOfWork::Solution sol)
{
solution = sol;
return completed = true;
});
for (unsigned i = 0; !completed; ++i)
{
if (current)
cnote << "Mining on PoWhash" << current.headerHash << ": " << f.miningProgress();
else
cnote << "Getting work package...";
Json::Value v = rpc.eth_getWork();
h256 hh(v[0].asString());
if (hh != current.headerHash)
{
current.headerHash = hh;
current.seedHash = h256(v[1].asString());
current.boundary = h256(fromHex(v[2].asString()), h256::AlignRight);
cnote << "Got work package:" << current.headerHash << " < " << current.boundary;
f.setWork(current);
}
this_thread::sleep_for(chrono::milliseconds(_recheckPeriod));
}
cnote << "Solution found; submitting [" << solution.nonce << "," << current.headerHash << "," << solution.mixHash << "] to" << _remote << "...";
bool ok = rpc.eth_submitWork("0x" + toString(solution.nonce), "0x" + toString(current.headerHash), "0x" + toString(solution.mixHash));
if (ok)
clog(HappyChannel) << "Submitted and accepted.";
else
clog(SadChannel) << "Not accepted.";
current.reset();
}
catch (jsonrpc::JsonRpcException&)
{
for (auto i = 3; --i; this_thread::sleep_for(chrono::seconds(1)))
cerr << "JSON-RPC problem. Probably couldn't connect. Retrying in " << i << "... \r";
cerr << endl;
}
#endif
exit(0);
}
void stopMiningAfterXBlocks(eth::Client* _c, unsigned _start, unsigned _mining) void stopMiningAfterXBlocks(eth::Client* _c, unsigned _start, unsigned _mining)
{ {
if (_c->isMining() && _c->blockChain().details().number - _start == _mining) if (_c->isMining() && _c->blockChain().details().number - _start == _mining)
@ -439,73 +248,6 @@ void stopMiningAfterXBlocks(eth::Client* _c, unsigned _start, unsigned _mining)
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
#if 0
cout << "\x1b[30mEthBlack\x1b[0m" << endl;
cout << "\x1b[90mEthCoal\x1b[0m" << endl;
cout << "\x1b[37mEthGray\x1b[0m" << endl;
cout << "\x1b[97mEthWhite\x1b[0m" << endl;
cout << "\x1b[31mEthRed\x1b[0m" << endl;
cout << "\x1b[32mEthGreen\x1b[0m" << endl;
cout << "\x1b[33mEthYellow\x1b[0m" << endl;
cout << "\x1b[34mEthBlue\x1b[0m" << endl;
cout << "\x1b[35mEthPurple\x1b[0m" << endl;
cout << "\x1b[36mEthCyan\x1b[0m" << endl;
// High Intensity
cout << "\x1b[91mEthRedI\x1b[0m" << endl;
cout << "\x1b[92mEthLime\x1b[0m" << endl;
cout << "\x1b[93mEthYellowI\x1b[0m" << endl;
cout << "\x1b[94mEthBlueI\x1b[0m" << endl;
cout << "\x1b[95mEthPurpleI\x1b[0m" << endl;
cout << "\x1b[96mEthCyanI\x1b[0m" << endl;
// Bold
cout << "\x1b[1;30mEthBlackB\x1b[0m" << endl;
cout << "\x1b[1;90mEthCoalB\x1b[0m" << endl;
cout << "\x1b[1;37mEthGrayB\x1b[0m" << endl;
cout << "\x1b[1;97mEthWhiteB\x1b[0m" << endl;
cout << "\x1b[1;31mEthRedB\x1b[0m" << endl;
cout << "\x1b[1;32mEthGreenB\x1b[0m" << endl;
cout << "\x1b[1;33mEthYellowB\x1b[0m" << endl;
cout << "\x1b[1;34mEthBlueB\x1b[0m" << endl;
cout << "\x1b[1;35mEthPurpleB\x1b[0m" << endl;
cout << "\x1b[1;36mEthCyanB\x1b[0m" << endl;
// Bold High Intensity
cout << "\x1b[1;91mEthRedBI\x1b[0m" << endl;
cout << "\x1b[1;92mEthGreenBI\x1b[0m" << endl;
cout << "\x1b[1;93mEthYellowBI\x1b[0m" << endl;
cout << "\x1b[1;94mEthBlueBI\x1b[0m" << endl;
cout << "\x1b[1;95mEthPurpleBI\x1b[0m" << endl;
cout << "\x1b[1;96mEthCyanBI\x1b[0m" << endl;
// Background
cout << "\x1b[40mEthBlackOn\x1b[0m" << endl;
cout << "\x1b[100mEthCoalOn\x1b[0m" << endl;
cout << "\x1b[47mEthGrayOn\x1b[0m" << endl;
cout << "\x1b[107mEthWhiteOn\x1b[0m" << endl;
cout << "\x1b[41mEthRedOn\x1b[0m" << endl;
cout << "\x1b[42mEthGreenOn\x1b[0m" << endl;
cout << "\x1b[43mEthYellowOn\x1b[0m" << endl;
cout << "\x1b[44mEthBlueOn\x1b[0m" << endl;
cout << "\x1b[45mEthPurpleOn\x1b[0m" << endl;
cout << "\x1b[46mEthCyanOn\x1b[0m" << endl;
// High Intensity backgrounds
cout << "\x1b[101mEthRedOnI\x1b[0m" << endl;
cout << "\x1b[102mEthGreenOnI\x1b[0m" << endl;
cout << "\x1b[103mEthYellowOnI\x1b[0m" << endl;
cout << "\x1b[104mEthBlueOnI\x1b[0m" << endl;
cout << "\x1b[105mEthPurpleOnI\x1b[0m" << endl;
cout << "\x1b[106mEthCyanOnI\x1b[0m" << endl;
// Underline
cout << "\x1b[4;30mEthBlackU\x1b[0m" << endl;
cout << "\x1b[4;31mEthRedU\x1b[0m" << endl;
cout << "\x1b[4;32mEthGreenU\x1b[0m" << endl;
cout << "\x1b[4;33mEthYellowU\x1b[0m" << endl;
cout << "\x1b[4;34mEthBlueU\x1b[0m" << endl;
cout << "\x1b[4;35mEthPurpleU\x1b[0m" << endl;
cout << "\x1b[4;36mEthCyanU\x1b[0m" << endl;
cout << "\x1b[4;37mEthWhiteU\x1b[0m" << endl;
#endif
// Init defaults // Init defaults
Defaults::get(); Defaults::get();
@ -513,12 +255,6 @@ int main(int argc, char** argv)
OperationMode mode = OperationMode::Node; OperationMode mode = OperationMode::Node;
string dbPath; string dbPath;
/// Mining options
MinerType minerType = MinerType::CPU;
unsigned openclPlatform = 0;
unsigned openclDevice = 0;
unsigned miningThreads = UINT_MAX;
/// File name for import/export. /// File name for import/export.
string filename; string filename;
@ -527,9 +263,6 @@ int main(int argc, char** argv)
string exportTo = "latest"; string exportTo = "latest";
Format exportFormat = Format::Binary; Format exportFormat = Format::Binary;
/// DAG initialisation param.
unsigned initDAG = 0;
/// General params for Node operation /// General params for Node operation
NodeMode nodeMode = NodeMode::Full; NodeMode nodeMode = NodeMode::Full;
bool interactive = false; bool interactive = false;
@ -547,7 +280,7 @@ int main(int argc, char** argv)
string publicIP; string publicIP;
string remoteHost; string remoteHost;
unsigned short remotePort = 30303; unsigned short remotePort = 30303;
unsigned peers = 5; unsigned peers = 11;
bool bootstrap = false; bool bootstrap = false;
unsigned networkId = 0; unsigned networkId = 0;
@ -568,19 +301,9 @@ int main(int argc, char** argv)
double etherPrice = 30.679; double etherPrice = 30.679;
double blockFees = 15.0; double blockFees = 15.0;
/// Benchmarking params
bool phoneHome = true;
unsigned benchmarkWarmup = 3;
unsigned benchmarkTrial = 3;
unsigned benchmarkTrials = 5;
// javascript console // javascript console
bool useConsole = false; bool useConsole = false;
/// Farm params
string farmURL = "http://127.0.0.1:8080";
unsigned farmRecheckPeriod = 500;
/// Wallet password stuff /// Wallet password stuff
string masterPassword; string masterPassword;
@ -602,10 +325,13 @@ int main(int argc, char** argv)
beneficiary = config[1].toHash<Address>(); beneficiary = config[1].toHash<Address>();
} }
MinerCLI m(MinerCLI::OperationMode::None);
for (int i = 1; i < argc; ++i) for (int i = 1; i < argc; ++i)
{ {
string arg = argv[i]; string arg = argv[i];
if (arg == "--listen-ip" && i + 1 < argc) if (m.interpretOption(i, argc, argv)) {}
else if (arg == "--listen-ip" && i + 1 < argc)
listenIP = argv[++i]; listenIP = argv[++i];
else if ((arg == "-l" || arg == "--listen" || arg == "--listen-port") && i + 1 < argc) else if ((arg == "-l" || arg == "--listen" || arg == "--listen-port") && i + 1 < argc)
{ {
@ -641,52 +367,6 @@ int main(int argc, char** argv)
mode = OperationMode::Export; mode = OperationMode::Export;
filename = argv[++i]; filename = argv[++i];
} }
else if ((arg == "-F" || arg == "--farm") && i + 1 < argc)
{
mode = OperationMode::Farm;
farmURL = argv[++i];
}
else if (arg == "--farm-recheck" && i + 1 < argc)
try {
farmRecheckPeriod = stol(argv[++i]);
}
catch (...)
{
cerr << "Bad " << arg << " option: " << argv[i] << endl;
return -1;
}
else if (arg == "--opencl-platform" && i + 1 < argc)
try {
openclPlatform = stol(argv[++i]);
}
catch (...)
{
cerr << "Bad " << arg << " option: " << argv[i] << endl;
return -1;
}
else if (arg == "--opencl-device" && i + 1 < argc)
try {
openclDevice = stol(argv[++i]);
miningThreads = 1;
}
catch (...)
{
cerr << "Bad " << arg << " option: " << argv[i] << endl;
return -1;
}
else if (arg == "--phone-home" && i + 1 < argc)
{
string m = argv[++i];
if (isTrue(m))
phoneHome = true;
else if (isFalse(m))
phoneHome = false;
else
{
cerr << "Bad " << arg << " option: " << m << endl;
return -1;
}
}
else if (arg == "--format" && i + 1 < argc) else if (arg == "--format" && i + 1 < argc)
{ {
string m = argv[++i]; string m = argv[++i];
@ -732,33 +412,6 @@ int main(int argc, char** argv)
cerr << "Bad " << arg << " option: " << argv[i] << endl; cerr << "Bad " << arg << " option: " << argv[i] << endl;
return -1; return -1;
} }
else if (arg == "--benchmark-warmup" && i + 1 < argc)
try {
benchmarkWarmup = stol(argv[++i]);
}
catch (...)
{
cerr << "Bad " << arg << " option: " << argv[i] << endl;
return -1;
}
else if (arg == "--benchmark-trial" && i + 1 < argc)
try {
benchmarkTrial = stol(argv[++i]);
}
catch (...)
{
cerr << "Bad " << arg << " option: " << argv[i] << endl;
return -1;
}
else if (arg == "--benchmark-trials" && i + 1 < argc)
try {
benchmarkTrials = stol(argv[++i]);
}
catch (...)
{
cerr << "Bad " << arg << " option: " << argv[i] << endl;
return -1;
}
else if (arg == "-K" || arg == "--kill-blockchain" || arg == "--kill") else if (arg == "-K" || arg == "--kill-blockchain" || arg == "--kill")
killChain = WithExisting::Kill; killChain = WithExisting::Kill;
else if (arg == "-R" || arg == "--rebuild") else if (arg == "-R" || arg == "--rebuild")
@ -783,14 +436,6 @@ int main(int argc, char** argv)
cerr << "Bad " << arg << " option: " << argv[i] << endl; cerr << "Bad " << arg << " option: " << argv[i] << endl;
return -1; return -1;
} }
else if (arg == "-C" || arg == "--cpu")
minerType = MinerType::CPU;
else if (arg == "-G" || arg == "--opencl")
minerType = MinerType::GPU;
/*<< " -s,--import-secret <secret> Import a secret key into the key store and use as the default." << endl
<< " -S,--import-session-secret <secret> Import a secret key into the key store and use as the default for this session only." << endl
<< " --sign-key <address> Sign all transactions with the key of the given address." << endl
<< " --session-sign-key <address> Sign all transactions with the key of the given address for this session only." << endl*/
else if ((arg == "-s" || arg == "--import-secret") && i + 1 < argc) else if ((arg == "-s" || arg == "--import-secret") && i + 1 < argc)
{ {
Secret s(fromHex(argv[++i])); Secret s(fromHex(argv[++i]));
@ -818,64 +463,6 @@ int main(int argc, char** argv)
} }
else if ((arg == "-d" || arg == "--path" || arg == "--db-path") && i + 1 < argc) else if ((arg == "-d" || arg == "--path" || arg == "--db-path") && i + 1 < argc)
dbPath = argv[++i]; dbPath = argv[++i];
else if ((arg == "-D" || arg == "--create-dag") && i + 1 < argc)
{
string m = boost::to_lower_copy(string(argv[++i]));
mode = OperationMode::DAGInit;
if (m == "next")
initDAG = PendingBlock;
else if (m == "this")
initDAG = LatestBlock;
else
try
{
initDAG = stol(m);
}
catch (...)
{
cerr << "Bad " << arg << " option: " << m << endl;
return -1;
}
}
else if ((arg == "-w" || arg == "--check-pow") && i + 4 < argc)
{
string m;
try
{
BlockInfo bi;
m = boost::to_lower_copy(string(argv[++i]));
h256 powHash(m);
m = boost::to_lower_copy(string(argv[++i]));
h256 seedHash;
if (m.size() == 64 || m.size() == 66)
seedHash = h256(m);
else
seedHash = EthashAux::seedHash(stol(m));
m = boost::to_lower_copy(string(argv[++i]));
bi.difficulty = u256(m);
auto boundary = bi.boundary();
m = boost::to_lower_copy(string(argv[++i]));
bi.nonce = h64(m);
auto r = EthashAux::eval((uint64_t)bi.number, powHash, bi.nonce);
bool valid = r.value < boundary;
cout << (valid ? "VALID :-)" : "INVALID :-(") << endl;
cout << r.value << (valid ? " < " : " >= ") << boundary << endl;
cout << " where " << boundary << " = 2^256 / " << bi.difficulty << endl;
cout << " and " << r.value << " = ethash(" << powHash << ", " << bi.nonce << ")" << endl;
cout << " with seed as " << seedHash << endl;
if (valid)
cout << "(mixHash = " << r.mixHash << ")" << endl;
cout << "SHA3( light(seed) ) = " << sha3(EthashAux::light((uint64_t)bi.number)->data()) << endl;
exit(0);
}
catch (...)
{
cerr << "Bad " << arg << " option: " << m << endl;
return -1;
}
}
else if (arg == "-M" || arg == "--benchmark")
mode = OperationMode::Benchmark;
else if ((arg == "-B" || arg == "--block-fees") && i + 1 < argc) else if ((arg == "-B" || arg == "--block-fees") && i + 1 < argc)
{ {
try try
@ -938,17 +525,6 @@ int main(int argc, char** argv)
return -1; return -1;
} }
} }
else if ((arg == "-t" || arg == "--mining-threads") && i + 1 < argc)
{
try {
miningThreads = stol(argv[++i]);
}
catch (...)
{
cerr << "Bad " << arg << " option: " << argv[i] << endl;
return -1;
}
}
else if (arg == "-b" || arg == "--bootstrap") else if (arg == "-b" || arg == "--bootstrap")
bootstrap = true; bootstrap = true;
else if (arg == "-f" || arg == "--force-mining") else if (arg == "-f" || arg == "--force-mining")
@ -999,15 +575,11 @@ int main(int argc, char** argv)
} }
} }
m.execute();
KeyManager keyManager; KeyManager keyManager;
for (auto const& s: passwordsToNote) for (auto const& s: passwordsToNote)
keyManager.notePassword(s); keyManager.notePassword(s);
for (auto const& s: toImport)
{
keyManager.import(s, "Imported key");
if (!signingKey)
signingKey = toAddress(s);
}
{ {
RLPStream config(2); RLPStream config(2);
@ -1018,26 +590,6 @@ int main(int argc, char** argv)
if (sessionKey) if (sessionKey)
signingKey = sessionKey; signingKey = sessionKey;
if (minerType == MinerType::CPU)
ProofOfWork::CPUMiner::setNumInstances(miningThreads);
else if (minerType == MinerType::GPU)
{
ProofOfWork::GPUMiner::setDefaultPlatform(openclPlatform);
ProofOfWork::GPUMiner::setDefaultDevice(openclDevice);
ProofOfWork::GPUMiner::setNumInstances(miningThreads);
}
// Two codepaths is necessary since named block require database, but numbered
// blocks are superuseful to have when database is already open in another process.
if (mode == OperationMode::DAGInit && !(initDAG == LatestBlock || initDAG == PendingBlock))
doInitDAG(initDAG);
if (mode == OperationMode::Benchmark)
doBenchmark(minerType, phoneHome, benchmarkWarmup, benchmarkTrial, benchmarkTrials);
if (mode == OperationMode::Farm)
doFarm(minerType, farmURL, farmRecheckPeriod);
if (!clientName.empty()) if (!clientName.empty())
clientName += "/"; clientName += "/";
@ -1076,34 +628,6 @@ int main(int argc, char** argv)
netPrefs, netPrefs,
&nodesState); &nodesState);
if (mode == OperationMode::DAGInit)
doInitDAG(web3.ethereum()->blockChain().number() + (initDAG == PendingBlock ? 30000 : 0));
if (keyManager.exists())
while (masterPassword.empty())
{
masterPassword = getPassword("Please enter your MASTER password: ");
if (!keyManager.load(masterPassword))
{
cout << "Password invalid. Try again." << endl;
masterPassword.clear();
}
}
else
{
while (masterPassword.empty())
{
masterPassword = getPassword("Please enter a MASTER password to protect your key store (make it strong!): ");
string confirm = getPassword("Please confirm the password by entering it again: ");
if (masterPassword != confirm)
{
cout << "Passwords were different. Try again." << endl;
masterPassword.clear();
}
}
keyManager.create(masterPassword);
}
auto toNumber = [&](string const& s) -> unsigned { auto toNumber = [&](string const& s) -> unsigned {
if (s == "latest") if (s == "latest")
return web3.ethereum()->number(); return web3.ethereum()->number();
@ -1167,7 +691,42 @@ int main(int argc, char** argv)
return 0; return 0;
} }
cout << credits(); if (keyManager.exists())
while (masterPassword.empty())
{
masterPassword = getPassword("Please enter your MASTER password: ");
if (!keyManager.load(masterPassword))
{
cout << "Password invalid. Try again." << endl;
masterPassword.clear();
}
}
else
{
while (masterPassword.empty())
{
masterPassword = getPassword("Please enter a MASTER password to protect your key store (make it strong!): ");
string confirm = getPassword("Please confirm the password by entering it again: ");
if (masterPassword != confirm)
{
cout << "Passwords were different. Try again." << endl;
masterPassword.clear();
}
}
keyManager.create(masterPassword);
}
for (auto const& s: toImport)
{
keyManager.import(s, "Imported key (UNSAFE)");
if (!signingKey)
signingKey = toAddress(s);
}
if (keyManager.accounts().empty())
keyManager.import(Secret::random(), "Default key");
cout << ethCredits();
web3.setIdealPeerCount(peers); web3.setIdealPeerCount(peers);
std::shared_ptr<eth::BasicGasPricer> gasPricer = make_shared<eth::BasicGasPricer>(u256(double(ether / 1000) / etherPrice), u256(blockFees * 1000)); std::shared_ptr<eth::BasicGasPricer> gasPricer = make_shared<eth::BasicGasPricer>(u256(double(ether / 1000) / etherPrice), u256(blockFees * 1000));
eth::Client* c = nodeMode == NodeMode::Full ? web3.ethereum() : nullptr; eth::Client* c = nodeMode == NodeMode::Full ? web3.ethereum() : nullptr;
@ -1176,7 +735,7 @@ int main(int argc, char** argv)
{ {
c->setGasPricer(gasPricer); c->setGasPricer(gasPricer);
c->setForceMining(forceMining); c->setForceMining(forceMining);
c->setTurboMining(minerType == MinerType::GPU); c->setTurboMining(m.minerType() == MinerCLI::MinerType::GPU);
c->setAddress(beneficiary); c->setAddress(beneficiary);
c->setNetworkId(networkId); c->setNetworkId(networkId);
} }
@ -1368,9 +927,36 @@ int main(int argc, char** argv)
<< std::chrono::duration_cast<std::chrono::milliseconds>(it.lastPing).count() << "ms" << std::chrono::duration_cast<std::chrono::milliseconds>(it.lastPing).count() << "ms"
<< endl; << endl;
} }
else if (c && cmd == "balance") else if (cmd == "newaccount")
{
string name;
std::getline(iss, name);
auto s = Secret::random();
string password;
while (password.empty())
{
password = getPassword("Please enter a password to protect this key (press enter for protection only be the MASTER password/keystore): ");
string confirm = getPassword("Please confirm the password by entering it again: ");
if (password != confirm)
{
cout << "Passwords were different. Try again." << endl;
password.clear();
}
}
if (!password.empty())
{
cout << "Enter a hint for this password: " << flush;
string hint;
std::getline(cin, hint);
keyManager.import(s, name, password, hint);
}
else
keyManager.import(s, name);
cout << "New account created: " << toAddress(s);
}
else if (c && cmd == "accounts")
{ {
cout << "Current balance:" << endl; cout << "Accounts:" << endl;
u256 total = 0; u256 total = 0;
for (auto const& i: keyManager.accountDetails()) for (auto const& i: keyManager.accountDetails())
{ {
@ -1765,7 +1351,7 @@ int main(int argc, char** argv)
if (useConsole) if (useConsole)
{ {
#if ETH_JSCONSOLE #if ETH_JSCONSOLE
JSConsole console(web3, vector<KeyPair>({sigKey})); JSConsole console(web3, make_shared<SimpleAccountHolder>([&](){return web3.ethereum();}, getAccountPassword, keyManager));
while (!g_exit) while (!g_exit)
{ {
console.repl(); console.repl();

7
ethminer/CMakeLists.txt

@ -17,12 +17,7 @@ add_dependencies(${EXECUTABLE} BuildInfo.h)
target_link_libraries(${EXECUTABLE} ${Boost_REGEX_LIBRARIES}) target_link_libraries(${EXECUTABLE} ${Boost_REGEX_LIBRARIES})
if (READLINE_FOUND)
target_link_libraries(${EXECUTABLE} ${READLINE_LIBRARIES})
endif()
if (JSONRPC) if (JSONRPC)
target_link_libraries(${EXECUTABLE} web3jsonrpc)
target_link_libraries(${EXECUTABLE} ${JSON_RPC_CPP_CLIENT_LIBRARIES}) target_link_libraries(${EXECUTABLE} ${JSON_RPC_CPP_CLIENT_LIBRARIES})
target_link_libraries(${EXECUTABLE} ${CURL_LIBRARIES}) target_link_libraries(${EXECUTABLE} ${CURL_LIBRARIES})
if (DEFINED WIN32 AND NOT DEFINED CMAKE_COMPILER_IS_MINGW) if (DEFINED WIN32 AND NOT DEFINED CMAKE_COMPILER_IS_MINGW)
@ -30,7 +25,7 @@ if (JSONRPC)
endif() endif()
endif() endif()
target_link_libraries(${EXECUTABLE} webthree) target_link_libraries(${EXECUTABLE} ethcore)
target_link_libraries(${EXECUTABLE} ethash) target_link_libraries(${EXECUTABLE} ethash)
if (DEFINED WIN32 AND NOT DEFINED CMAKE_COMPILER_IS_MINGW) if (DEFINED WIN32 AND NOT DEFINED CMAKE_COMPILER_IS_MINGW)

488
ethminer/MinerAux.h

@ -0,0 +1,488 @@
#pragma once
/*
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
* Ethereum client.
*/
#include <thread>
#include <chrono>
#include <fstream>
#include <iostream>
#include <signal.h>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/trim_all.hpp>
#include <libdevcrypto/FileSystem.h>
#include <libevmcore/Instruction.h>
#include <libdevcore/StructuredLogger.h>
#include <libethcore/Exceptions.h>
#include <libdevcrypto/SHA3.h>
#include <libethcore/ProofOfWork.h>
#include <libethcore/EthashAux.h>
#include <libethcore/Farm.h>
#if ETH_JSONRPC || !ETH_TRUE
#include <libweb3jsonrpc/WebThreeStubServer.h>
#include <jsonrpccpp/server/connectors/httpserver.h>
#include <jsonrpccpp/client/connectors/httpclient.h>
#endif
#include "BuildInfo.h"
#if ETH_JSONRPC || !ETH_TRUE
#include "PhoneHome.h"
#include "Farm.h"
#endif
using namespace std;
using namespace dev;
using namespace dev::eth;
using namespace boost::algorithm;
using dev::eth::Instruction;
#undef RETURN
bool isTrue(std::string const& _m)
{
return _m == "on" || _m == "yes" || _m == "true" || _m == "1";
}
bool isFalse(std::string const& _m)
{
return _m == "off" || _m == "no" || _m == "false" || _m == "0";
}
inline std::string credits()
{
std::ostringstream out;
out
<< "Ethereum (++) " << dev::Version << endl
<< " Code by Gav Wood et al, (c) 2013, 2014, 2015." << endl;
return out.str();
}
class BadArgument: public Exception {};
class MinerCLI
{
public:
enum class OperationMode
{
None,
DAGInit,
Benchmark,
Farm
};
MinerCLI(OperationMode _mode = OperationMode::None): mode(_mode) {}
bool interpretOption(int& i, int argc, char** argv)
{
string arg = argv[i];
if ((arg == "-F" || arg == "--farm") && i + 1 < argc)
{
mode = OperationMode::Farm;
farmURL = argv[++i];
}
else if (arg == "--farm-recheck" && i + 1 < argc)
try {
farmRecheckPeriod = stol(argv[++i]);
}
catch (...)
{
cerr << "Bad " << arg << " option: " << argv[i] << endl;
throw BadArgument();
}
else if (arg == "--opencl-platform" && i + 1 < argc)
try {
openclPlatform = stol(argv[++i]);
}
catch (...)
{
cerr << "Bad " << arg << " option: " << argv[i] << endl;
throw BadArgument();
}
else if (arg == "--opencl-device" && i + 1 < argc)
try {
openclDevice = stol(argv[++i]);
miningThreads = 1;
}
catch (...)
{
cerr << "Bad " << arg << " option: " << argv[i] << endl;
throw BadArgument();
}
else if (arg == "--phone-home" && i + 1 < argc)
{
string m = argv[++i];
if (isTrue(m))
phoneHome = true;
else if (isFalse(m))
phoneHome = false;
else
{
cerr << "Bad " << arg << " option: " << m << endl;
throw BadArgument();
}
}
else if (arg == "--benchmark-warmup" && i + 1 < argc)
try {
benchmarkWarmup = stol(argv[++i]);
}
catch (...)
{
cerr << "Bad " << arg << " option: " << argv[i] << endl;
throw BadArgument();
}
else if (arg == "--benchmark-trial" && i + 1 < argc)
try {
benchmarkTrial = stol(argv[++i]);
}
catch (...)
{
cerr << "Bad " << arg << " option: " << argv[i] << endl;
throw BadArgument();
}
else if (arg == "--benchmark-trials" && i + 1 < argc)
try {
benchmarkTrials = stol(argv[++i]);
}
catch (...)
{
cerr << "Bad " << arg << " option: " << argv[i] << endl;
throw BadArgument();
}
else if (arg == "-C" || arg == "--cpu")
m_minerType = MinerType::CPU;
else if (arg == "-G" || arg == "--opencl")
{
m_minerType = MinerType::GPU;
miningThreads = 1;
}
else if ((arg == "-D" || arg == "--create-dag") && i + 1 < argc)
{
string m = boost::to_lower_copy(string(argv[++i]));
mode = OperationMode::DAGInit;
try
{
initDAG = stol(m);
}
catch (...)
{
cerr << "Bad " << arg << " option: " << m << endl;
throw BadArgument();
}
}
else if ((arg == "-w" || arg == "--check-pow") && i + 4 < argc)
{
string m;
try
{
BlockInfo bi;
m = boost::to_lower_copy(string(argv[++i]));
h256 powHash(m);
m = boost::to_lower_copy(string(argv[++i]));
h256 seedHash;
if (m.size() == 64 || m.size() == 66)
seedHash = h256(m);
else
seedHash = EthashAux::seedHash(stol(m));
m = boost::to_lower_copy(string(argv[++i]));
bi.difficulty = u256(m);
auto boundary = bi.boundary();
m = boost::to_lower_copy(string(argv[++i]));
bi.nonce = h64(m);
auto r = EthashAux::eval(bi.seedHash(), powHash, bi.nonce);
bool valid = r.value < boundary;
cout << (valid ? "VALID :-)" : "INVALID :-(") << endl;
cout << r.value << (valid ? " < " : " >= ") << boundary << endl;
cout << " where " << boundary << " = 2^256 / " << bi.difficulty << endl;
cout << " and " << r.value << " = ethash(" << powHash << ", " << bi.nonce << ")" << endl;
cout << " with seed as " << seedHash << endl;
if (valid)
cout << "(mixHash = " << r.mixHash << ")" << endl;
cout << "SHA3( light(seed) ) = " << sha3(EthashAux::light(bi.seedHash())->data()) << endl;
exit(0);
}
catch (...)
{
cerr << "Bad " << arg << " option: " << m << endl;
throw BadArgument();
}
}
else if (arg == "-M" || arg == "--benchmark")
mode = OperationMode::Benchmark;
else if ((arg == "-t" || arg == "--mining-threads") && i + 1 < argc)
{
try {
miningThreads = stol(argv[++i]);
}
catch (...)
{
cerr << "Bad " << arg << " option: " << argv[i] << endl;
throw BadArgument();
}
}
else
return false;
return true;
}
void execute()
{
if (m_minerType == MinerType::CPU)
ProofOfWork::CPUMiner::setNumInstances(miningThreads);
else if (m_minerType == MinerType::GPU)
{
ProofOfWork::GPUMiner::setDefaultPlatform(openclPlatform);
ProofOfWork::GPUMiner::setDefaultDevice(openclDevice);
ProofOfWork::GPUMiner::setNumInstances(miningThreads);
}
if (mode == OperationMode::DAGInit)
doInitDAG(initDAG);
else if (mode == OperationMode::Benchmark)
doBenchmark(m_minerType, phoneHome, benchmarkWarmup, benchmarkTrial, benchmarkTrials);
else if (mode == OperationMode::Farm)
doFarm(m_minerType, farmURL, farmRecheckPeriod);
}
static void streamHelp(ostream& _out)
{
_out
#if ETH_JSONRPC || !ETH_TRUE
<< "Work farming mode:" << endl
<< " -F,--farm <url> Put into mining farm mode with the work server at URL (default: http://127.0.0.1:8545)" << endl
<< " --farm-recheck <n> Leave n ms between checks for changed work (default: 500)." << endl
#endif
<< "Ethash verify mode:" << endl
<< " -w,--check-pow <headerHash> <seedHash> <difficulty> <nonce> Check PoW credentials for validity." << endl
<< endl
<< "Benchmarking mode:" << endl
<< " -M,--benchmark Benchmark for mining and exit; use with --cpu and --opencl." << endl
<< " --benchmark-warmup <seconds> Set the duration of warmup for the benchmark tests (default: 3)." << endl
<< " --benchmark-trial <seconds> Set the duration for each trial for the benchmark tests (default: 3)." << endl
<< " --benchmark-trials <n> Set the duration of warmup for the benchmark tests (default: 5)." << endl
#if ETH_JSONRPC || !ETH_TRUE
<< " --phone-home <on/off> When benchmarking, publish results (default: on)" << endl
#endif
<< "DAG creation mode:" << endl
<< " -D,--create-dag <number> Create the DAG in preparation for mining on given block and exit." << endl
<< "Mining configuration:" << endl
<< " -C,--cpu When mining, use the CPU." << endl
<< " -G,--opencl When mining use the GPU via OpenCL." << endl
<< " --opencl-platform <n> When mining using -G/--opencl use OpenCL platform n (default: 0)." << endl
<< " --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
;
}
enum class MinerType
{
CPU,
GPU
};
MinerType minerType() const { return m_minerType; }
private:
void doInitDAG(unsigned _n)
{
BlockInfo bi;
bi.number = _n;
cout << "Initializing DAG for epoch beginning #" << (bi.number / 30000 * 30000) << " (seedhash " << bi.seedHash().abridged() << "). This will take a while." << endl;
Ethash::prep(bi);
exit(0);
}
void doBenchmark(MinerType _m, bool _phoneHome, unsigned _warmupDuration = 15, unsigned _trialDuration = 3, unsigned _trials = 5)
{
BlockInfo genesis;
genesis.difficulty = 1 << 18;
cdebug << genesis.boundary();
GenericFarm<Ethash> f;
f.onSolutionFound([&](ProofOfWork::Solution) { return false; });
string platformInfo = _m == MinerType::CPU ? ProofOfWork::CPUMiner::platformInfo() : _m == MinerType::GPU ? ProofOfWork::GPUMiner::platformInfo() : "";
cout << "Benchmarking on platform: " << platformInfo << endl;
cout << "Preparing DAG..." << endl;
Ethash::prep(genesis);
genesis.difficulty = u256(1) << 63;
genesis.noteDirty();
f.setWork(genesis);
if (_m == MinerType::CPU)
f.startCPU();
else if (_m == MinerType::GPU)
f.startGPU();
map<uint64_t, MiningProgress> results;
uint64_t mean = 0;
uint64_t innerMean = 0;
for (unsigned i = 0; i <= _trials; ++i)
{
if (!i)
cout << "Warming up..." << endl;
else
cout << "Trial " << i << "... " << flush;
this_thread::sleep_for(chrono::seconds(i ? _trialDuration : _warmupDuration));
auto mp = f.miningProgress();
f.resetMiningProgress();
if (!i)
continue;
auto rate = mp.rate();
cout << rate << endl;
results[rate] = mp;
mean += rate;
}
f.stop();
int j = -1;
for (auto const& r: results)
if (++j > 0 && j < (int)_trials - 1)
innerMean += r.second.rate();
innerMean /= (_trials - 2);
cout << "min/mean/max: " << results.begin()->second.rate() << "/" << (mean / _trials) << "/" << results.rbegin()->second.rate() << " H/s" << endl;
cout << "inner mean: " << innerMean << " H/s" << endl;
(void)_phoneHome;
#if ETH_JSONRPC || !ETH_TRUE
if (_phoneHome)
{
cout << "Phoning home to find world ranking..." << endl;
jsonrpc::HttpClient client("http://gav.ethdev.com:3000");
PhoneHome rpc(client);
try
{
unsigned ranking = rpc.report_benchmark(platformInfo, innerMean);
cout << "Ranked: " << ranking << " of all benchmarks." << endl;
}
catch (...)
{
cout << "Error phoning home. ET is sad." << endl;
}
}
#endif
exit(0);
}
void doFarm(MinerType _m, string const& _remote, unsigned _recheckPeriod)
{
(void)_m;
(void)_remote;
(void)_recheckPeriod;
#if ETH_JSONRPC || !ETH_TRUE
jsonrpc::HttpClient client(_remote);
Farm rpc(client);
GenericFarm<Ethash> f;
if (_m == MinerType::CPU)
f.startCPU();
else if (_m == MinerType::GPU)
f.startGPU();
ProofOfWork::WorkPackage current;
EthashAux::FullType dag;
while (true)
try
{
bool completed = false;
ProofOfWork::Solution solution;
f.onSolutionFound([&](ProofOfWork::Solution sol)
{
solution = sol;
return completed = true;
});
for (unsigned i = 0; !completed; ++i)
{
if (current)
cnote << "Mining on PoWhash" << current.headerHash << ": " << f.miningProgress();
else
cnote << "Getting work package...";
Json::Value v = rpc.eth_getWork();
h256 hh(v[0].asString());
h256 newSeedHash(v[1].asString());
if (!(dag = EthashAux::full(newSeedHash, true)))
BOOST_THROW_EXCEPTION(DAGCreationFailure());
if (hh != current.headerHash)
{
current.headerHash = hh;
current.seedHash = newSeedHash;
current.boundary = h256(fromHex(v[2].asString()), h256::AlignRight);
cnote << "Got work package:";
cnote << " Header-hash:" << current.headerHash.hex();
cnote << " Seedhash:" << current.seedHash.hex();
cnote << " Target: " << h256(current.boundary).hex();
f.setWork(current);
}
this_thread::sleep_for(chrono::milliseconds(_recheckPeriod));
}
cnote << "Solution found; Submitting to" << _remote << "...";
cnote << " Nonce:" << solution.nonce.hex();
cnote << " Mixhash:" << solution.mixHash.hex();
cnote << " Header-hash:" << current.headerHash.hex();
cnote << " Seedhash:" << current.seedHash.hex();
cnote << " Target: " << h256(current.boundary).hex();
cnote << " Ethash: " << h256(EthashAux::eval(current.seedHash, current.headerHash, solution.nonce).value).hex();
if (EthashAux::eval(current.seedHash, current.headerHash, solution.nonce).value < current.boundary)
{
bool ok = rpc.eth_submitWork("0x" + toString(solution.nonce), "0x" + toString(current.headerHash), "0x" + toString(solution.mixHash));
if (ok)
cnote << "B-) Submitted and accepted.";
else
cwarn << ":-( Not accepted.";
}
else
cwarn << "FAILURE: GPU gave incorrect result!";
current.reset();
}
catch (jsonrpc::JsonRpcException&)
{
for (auto i = 3; --i; this_thread::sleep_for(chrono::seconds(1)))
cerr << "JSON-RPC problem. Probably couldn't connect. Retrying in " << i << "... \r";
cerr << endl;
}
#endif
exit(0);
}
/// Operating mode.
OperationMode mode;
/// Mining options
MinerType m_minerType = MinerType::CPU;
unsigned openclPlatform = 0;
unsigned openclDevice = 0;
unsigned miningThreads = UINT_MAX;
/// DAG initialisation param.
unsigned initDAG = 0;
/// Benchmarking params
bool phoneHome = true;
unsigned benchmarkWarmup = 3;
unsigned benchmarkTrial = 3;
unsigned benchmarkTrials = 5;
/// Farm params
string farmURL = "http://127.0.0.1:8545";
unsigned farmRecheckPeriod = 500;
};

417
ethminer/main.cpp

@ -25,429 +25,48 @@
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <signal.h> #include <signal.h>
#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 <libdevcrypto/FileSystem.h> #include <libdevcrypto/FileSystem.h>
#include <libevmcore/Instruction.h> #include "MinerAux.h"
#include <libdevcore/StructuredLogger.h>
#include <libethcore/ProofOfWork.h>
#include <libethcore/EthashAux.h>
#include <libevm/VM.h>
#include <libevm/VMFactory.h>
#include <libethereum/All.h>
#include <libwebthree/WebThree.h>
#if ETH_JSONRPC || !ETH_TRUE
#include <libweb3jsonrpc/WebThreeStubServer.h>
#include <jsonrpccpp/server/connectors/httpserver.h>
#include <jsonrpccpp/client/connectors/httpclient.h>
#endif
#include "BuildInfo.h"
#if ETH_JSONRPC || !ETH_TRUE
#include "PhoneHome.h"
#include "Farm.h"
#endif
using namespace std; using namespace std;
using namespace dev; using namespace dev;
using namespace dev::p2p;
using namespace dev::eth; using namespace dev::eth;
using namespace boost::algorithm; using namespace boost::algorithm;
using dev::eth::Instruction;
#undef RETURN #undef RETURN
bool isTrue(std::string const& _m)
{
return _m == "on" || _m == "yes" || _m == "true" || _m == "1";
}
bool isFalse(std::string const& _m)
{
return _m == "off" || _m == "no" || _m == "false" || _m == "0";
}
void help() void help()
{ {
cout cout
<< "Usage ethminer [OPTIONS]" << endl << "Usage ethminer [OPTIONS]" << endl
<< "Options:" << endl << endl << "Options:" << endl << endl;
#if ETH_JSONRPC || !ETH_TRUE MinerCLI::streamHelp(cout);
<< "Work farming mode:" << endl cout
<< " -F,--farm <url> Put into mining farm mode with the work server at URL (default: http://127.0.0.1:8080)" << endl
<< " --farm-recheck <n> Leave n ms between checks for changed work (default: 500)." << endl
#endif
<< "Benchmarking mode:" << endl
<< " -M,--benchmark Benchmark for mining and exit; use with --cpu and --opencl." << endl
<< " --benchmark-warmup <seconds> Set the duration of warmup for the benchmark tests (default: 3)." << endl
<< " --benchmark-trial <seconds> Set the duration for each trial for the benchmark tests (default: 3)." << endl
<< " --benchmark-trials <n> Set the duration of warmup for the benchmark tests (default: 5)." << endl
#if ETH_JSONRPC || !ETH_TRUE
<< " --phone-home <on/off> When benchmarking, publish results (default: on)" << endl
#endif
<< "DAG creation mode:" << endl
<< " -D,--create-dag <number> Create the DAG in preparation for mining on given block and exit." << endl
<< "General Options:" << endl << "General Options:" << endl
<< " -C,--cpu When mining, use the CPU." << endl
<< " -G,--opencl When mining use the GPU via OpenCL." << endl
<< " --opencl-platform <n> When mining using -G/--opencl use OpenCL platform n (default: 0)." << endl
<< " --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
<< " -v,--verbosity <0 - 9> Set the log verbosity from 0 to 9 (default: 8)." << endl << " -v,--verbosity <0 - 9> Set the log verbosity from 0 to 9 (default: 8)." << endl
<< " -V,--version Show the version and exit." << endl << " -V,--version Show the version and exit." << endl
<< " -h,--help Show this help message and exit." << endl << " -h,--help Show this help message and exit." << endl
; ;
exit(0); exit(0);
}
string credits()
{
std::ostringstream cout;
cout
<< "Ethereum (++) " << dev::Version << endl
<< " Code by Gav Wood et al, (c) 2013, 2014, 2015." << endl
<< " Based on a design by Vitalik Buterin." << endl << endl;
return cout.str();
} }
void version() void version()
{ {
cout << "eth version " << dev::Version << endl; cout << "ethminer version " << dev::Version << endl;
cout << "eth network protocol version: " << dev::eth::c_protocolVersion << endl;
cout << "Client database version: " << dev::eth::c_databaseVersion << endl;
cout << "Build: " << DEV_QUOTED(ETH_BUILD_PLATFORM) << "/" << DEV_QUOTED(ETH_BUILD_TYPE) << endl; cout << "Build: " << DEV_QUOTED(ETH_BUILD_PLATFORM) << "/" << DEV_QUOTED(ETH_BUILD_TYPE) << endl;
exit(0); exit(0);
} }
void doInitDAG(unsigned _n)
{
BlockInfo bi;
bi.number = _n;
cout << "Initializing DAG for epoch beginning #" << (bi.number / 30000 * 30000) << " (seedhash " << bi.seedHash().abridged() << "). This will take a while." << endl;
Ethash::prep(bi);
exit(0);
}
enum class OperationMode
{
DAGInit,
Benchmark,
Farm
};
enum class MinerType
{
CPU,
GPU
};
void doBenchmark(MinerType _m, bool _phoneHome, unsigned _warmupDuration = 15, unsigned _trialDuration = 3, unsigned _trials = 5)
{
BlockInfo genesis = CanonBlockChain::genesis();
genesis.difficulty = 1 << 18;
cdebug << genesis.boundary();
GenericFarm<Ethash> f;
f.onSolutionFound([&](ProofOfWork::Solution) { return false; });
string platformInfo = _m == MinerType::CPU ? ProofOfWork::CPUMiner::platformInfo() : _m == MinerType::GPU ? ProofOfWork::GPUMiner::platformInfo() : "";
cout << "Benchmarking on platform: " << platformInfo << endl;
cout << "Preparing DAG..." << endl;
Ethash::prep(genesis);
genesis.difficulty = u256(1) << 63;
genesis.noteDirty();
f.setWork(genesis);
if (_m == MinerType::CPU)
f.startCPU();
else if (_m == MinerType::GPU)
f.startGPU();
map<uint64_t, MiningProgress> results;
uint64_t mean = 0;
uint64_t innerMean = 0;
for (unsigned i = 0; i <= _trials; ++i)
{
if (!i)
cout << "Warming up..." << endl;
else
cout << "Trial " << i << "... " << flush;
this_thread::sleep_for(chrono::seconds(i ? _trialDuration : _warmupDuration));
auto mp = f.miningProgress();
f.resetMiningProgress();
if (!i)
continue;
auto rate = mp.rate();
cout << rate << endl;
results[rate] = mp;
mean += rate;
if (i > 1 && i < 5)
innerMean += rate;
}
f.stop();
innerMean /= (_trials - 2);
cout << "min/mean/max: " << results.begin()->second.rate() << "/" << (mean / _trials) << "/" << results.rbegin()->second.rate() << " H/s" << endl;
cout << "inner mean: " << innerMean << " H/s" << endl;
(void)_phoneHome;
#if ETH_JSONRPC || !ETH_TRUE
if (_phoneHome)
{
cout << "Phoning home to find world ranking..." << endl;
jsonrpc::HttpClient client("http://gav.ethdev.com:3000/benchmark");
PhoneHome rpc(client);
try
{
unsigned ranking = rpc.report_benchmark(platformInfo, innerMean);
cout << "Ranked: " << ranking << " of all benchmarks." << endl;
}
catch (...)
{
cout << "Error phoning home. ET is sad." << endl;
}
}
#endif
exit(0);
}
struct HappyChannel: public LogChannel { static const char* name() { return ":-D"; } static const int verbosity = 1; };
struct SadChannel: public LogChannel { static const char* name() { return ":-("; } static const int verbosity = 1; };
void doFarm(MinerType _m, string const& _remote, unsigned _recheckPeriod)
{
(void)_m;
(void)_remote;
(void)_recheckPeriod;
#if ETH_JSONRPC || !ETH_TRUE
jsonrpc::HttpClient client(_remote);
Farm rpc(client);
GenericFarm<Ethash> f;
if (_m == MinerType::CPU)
f.startCPU();
else if (_m == MinerType::GPU)
f.startGPU();
ProofOfWork::WorkPackage current;
while (true)
try
{
bool completed = false;
ProofOfWork::Solution solution;
f.onSolutionFound([&](ProofOfWork::Solution sol)
{
solution = sol;
return completed = true;
});
for (unsigned i = 0; !completed; ++i)
{
if (current)
cnote << "Mining on PoWhash" << current.headerHash << ": " << f.miningProgress();
else
cnote << "Getting work package...";
Json::Value v = rpc.eth_getWork();
h256 hh(v[0].asString());
if (hh != current.headerHash)
{
current.headerHash = hh;
current.seedHash = h256(v[1].asString());
current.boundary = h256(fromHex(v[2].asString()), h256::AlignRight);
cnote << "Got work package:" << current.headerHash << " < " << current.boundary;
f.setWork(current);
}
this_thread::sleep_for(chrono::milliseconds(_recheckPeriod));
}
cnote << "Solution found; submitting [" << solution.nonce << "," << current.headerHash << "," << solution.mixHash << "] to" << _remote << "...";
bool ok = rpc.eth_submitWork("0x" + toString(solution.nonce), "0x" + toString(current.headerHash), "0x" + toString(solution.mixHash));
if (ok)
clog(HappyChannel) << "Submitted and accepted.";
else
clog(SadChannel) << "Not accepted.";
current.reset();
}
catch (jsonrpc::JsonRpcException&)
{
for (auto i = 3; --i; this_thread::sleep_for(chrono::seconds(1)))
cerr << "JSON-RPC problem. Probably couldn't connect. Retrying in " << i << "... \r";
cerr << endl;
}
#endif
exit(0);
}
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
// Init defaults MinerCLI m(MinerCLI::OperationMode::Farm);
Defaults::get();
/// Operating mode.
OperationMode mode = OperationMode::Farm;
/// Mining options
MinerType minerType = MinerType::CPU;
unsigned openclPlatform = 0;
unsigned openclDevice = 0;
unsigned miningThreads = UINT_MAX;
/// DAG initialisation param.
unsigned initDAG = 0;
/// Benchmarking params
bool phoneHome = true;
unsigned benchmarkWarmup = 3;
unsigned benchmarkTrial = 3;
unsigned benchmarkTrials = 5;
/// Farm params
string farmURL = "http://127.0.0.1:8080";
unsigned farmRecheckPeriod = 500;
for (int i = 1; i < argc; ++i) for (int i = 1; i < argc; ++i)
{ {
string arg = argv[i]; string arg = argv[i];
if ((arg == "-F" || arg == "--farm") && i + 1 < argc) if (m.interpretOption(i, argc, argv))
{ {}
mode = OperationMode::Farm;
farmURL = argv[++i];
}
else if (arg == "--farm-recheck" && i + 1 < argc)
try {
farmRecheckPeriod = stol(argv[++i]);
}
catch (...)
{
cerr << "Bad " << arg << " option: " << argv[i] << endl;
return -1;
}
else if (arg == "--opencl-platform" && i + 1 < argc)
try {
openclPlatform = stol(argv[++i]);
}
catch (...)
{
cerr << "Bad " << arg << " option: " << argv[i] << endl;
return -1;
}
else if (arg == "--opencl-device" && i + 1 < argc)
try {
openclDevice = stol(argv[++i]);
miningThreads = 1;
}
catch (...)
{
cerr << "Bad " << arg << " option: " << argv[i] << endl;
return -1;
}
else if (arg == "--phone-home" && i + 1 < argc)
{
string m = argv[++i];
if (isTrue(m))
phoneHome = true;
else if (isFalse(m))
phoneHome = false;
else
{
cerr << "Bad " << arg << " option: " << m << endl;
return -1;
}
}
else if (arg == "--benchmark-warmup" && i + 1 < argc)
try {
benchmarkWarmup = stol(argv[++i]);
}
catch (...)
{
cerr << "Bad " << arg << " option: " << argv[i] << endl;
return -1;
}
else if (arg == "--benchmark-trial" && i + 1 < argc)
try {
benchmarkTrial = stol(argv[++i]);
}
catch (...)
{
cerr << "Bad " << arg << " option: " << argv[i] << endl;
return -1;
}
else if (arg == "--benchmark-trials" && i + 1 < argc)
try {
benchmarkTrials = stol(argv[++i]);
}
catch (...)
{
cerr << "Bad " << arg << " option: " << argv[i] << endl;
return -1;
}
else if (arg == "-C" || arg == "--cpu")
minerType = MinerType::CPU;
else if (arg == "-G" || arg == "--opencl")
minerType = MinerType::GPU;
else if ((arg == "-D" || arg == "--create-dag") && i + 1 < argc)
{
string m = boost::to_lower_copy(string(argv[++i]));
mode = OperationMode::DAGInit;
try
{
initDAG = stol(m);
}
catch (...)
{
cerr << "Bad " << arg << " option: " << m << endl;
return -1;
}
}
else if ((arg == "-w" || arg == "--check-pow") && i + 4 < argc)
{
string m;
try
{
BlockInfo bi;
m = boost::to_lower_copy(string(argv[++i]));
h256 powHash(m);
m = boost::to_lower_copy(string(argv[++i]));
h256 seedHash;
if (m.size() == 64 || m.size() == 66)
seedHash = h256(m);
else
seedHash = EthashAux::seedHash(stol(m));
m = boost::to_lower_copy(string(argv[++i]));
bi.difficulty = u256(m);
auto boundary = bi.boundary();
m = boost::to_lower_copy(string(argv[++i]));
bi.nonce = h64(m);
auto r = EthashAux::eval((uint64_t)bi.number, powHash, bi.nonce);
bool valid = r.value < boundary;
cout << (valid ? "VALID :-)" : "INVALID :-(") << endl;
cout << r.value << (valid ? " < " : " >= ") << boundary << endl;
cout << " where " << boundary << " = 2^256 / " << bi.difficulty << endl;
cout << " and " << r.value << " = ethash(" << powHash << ", " << bi.nonce << ")" << endl;
cout << " with seed as " << seedHash << endl;
if (valid)
cout << "(mixHash = " << r.mixHash << ")" << endl;
cout << "SHA3( light(seed) ) = " << sha3(EthashAux::light((uint64_t)bi.number)->data()) << endl;
exit(0);
}
catch (...)
{
cerr << "Bad " << arg << " option: " << m << endl;
return -1;
}
}
else if (arg == "-M" || arg == "--benchmark")
mode = OperationMode::Benchmark;
else if ((arg == "-t" || arg == "--mining-threads") && i + 1 < argc)
{
try {
miningThreads = stol(argv[++i]);
}
catch (...)
{
cerr << "Bad " << arg << " option: " << argv[i] << endl;
return -1;
}
}
else if ((arg == "-v" || arg == "--verbosity") && i + 1 < argc) else if ((arg == "-v" || arg == "--verbosity") && i + 1 < argc)
g_logVerbosity = atoi(argv[++i]); g_logVerbosity = atoi(argv[++i]);
else if (arg == "-h" || arg == "--help") else if (arg == "-h" || arg == "--help")
@ -461,23 +80,7 @@ int main(int argc, char** argv)
} }
} }
if (minerType == MinerType::CPU) m.execute();
ProofOfWork::CPUMiner::setNumInstances(miningThreads);
else if (minerType == MinerType::GPU)
{
ProofOfWork::GPUMiner::setDefaultPlatform(openclPlatform);
ProofOfWork::GPUMiner::setDefaultDevice(openclDevice);
ProofOfWork::GPUMiner::setNumInstances(miningThreads);
}
if (mode == OperationMode::DAGInit)
doInitDAG(initDAG);
if (mode == OperationMode::Benchmark)
doBenchmark(minerType, phoneHome, benchmarkWarmup, benchmarkTrial, benchmarkTrials);
if (mode == OperationMode::Farm)
doFarm(minerType, farmURL, farmRecheckPeriod);
return 0; return 0;
} }

2
exp/main.cpp

@ -45,10 +45,10 @@
#include <libdevcrypto/SecretStore.h> #include <libdevcrypto/SecretStore.h>
#include <libp2p/All.h> #include <libp2p/All.h>
#include <libethcore/ProofOfWork.h> #include <libethcore/ProofOfWork.h>
#include <libethcore/Farm.h>
#include <libdevcrypto/FileSystem.h> #include <libdevcrypto/FileSystem.h>
#include <libethereum/All.h> #include <libethereum/All.h>
#include <libethereum/KeyManager.h> #include <libethereum/KeyManager.h>
#include <libethereum/Farm.h>
#include <libethereum/AccountDiff.h> #include <libethereum/AccountDiff.h>
#include <libethereum/DownloadMan.h> #include <libethereum/DownloadMan.h>
#include <libethereum/Client.h> #include <libethereum/Client.h>

2
libdevcore/Common.cpp

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

45
libdevcore/CommonIO.cpp

@ -24,6 +24,12 @@
#include <cstdlib> #include <cstdlib>
#include <fstream> #include <fstream>
#include "Exceptions.h" #include "Exceptions.h"
#include <stdio.h>
#ifdef _WIN32
#include <windows.h>
#else
#include <termios.h>
#endif
using namespace std; using namespace std;
using namespace dev; using namespace dev;
@ -122,10 +128,47 @@ std::string dev::getPassword(std::string const& _prompt)
{ {
#if WIN32 #if WIN32
cout << _prompt << flush; cout << _prompt << flush;
// Get current Console input flags
HANDLE hStdin;
DWORD fdwSaveOldMode;
if ((hStdin = GetStdHandle(STD_INPUT_HANDLE)) == INVALID_HANDLE_VALUE)
BOOST_THROW_EXCEPTION(ExternalFunctionFailure("GetStdHandle"));
if (!GetConsoleMode(hStdin, &fdwSaveOldMode))
BOOST_THROW_EXCEPTION(ExternalFunctionFailure("GetConsoleMode"));
// Set console flags to no echo
if (!SetConsoleMode(hStdin, fdwSaveOldMode & (~ENABLE_ECHO_INPUT)))
BOOST_THROW_EXCEPTION(ExternalFunctionFailure("SetConsoleMode"));
// Read the string
std::string ret; std::string ret;
std::getline(cin, ret); std::getline(cin, ret);
// Restore old input mode
if (!SetConsoleMode(hStdin, fdwSaveOldMode))
BOOST_THROW_EXCEPTION(ExternalFunctionFailure("SetConsoleMode"));
return ret; return ret;
#else #else
return getpass(_prompt.c_str()); struct termios oflags;
struct termios nflags;
char password[256];
// disable echo in the terminal
tcgetattr(fileno(stdin), &oflags);
nflags = oflags;
nflags.c_lflag &= ~ECHO;
nflags.c_lflag |= ECHONL;
if (tcsetattr(fileno(stdin), TCSANOW, &nflags) != 0)
BOOST_THROW_EXCEPTION(ExternalFunctionFailure("tcsetattr"));
printf("%s", _prompt.c_str());
if (!fgets(password, sizeof(password), stdin))
BOOST_THROW_EXCEPTION(ExternalFunctionFailure("fgets"));
password[strlen(password) - 1] = 0;
// restore terminal
if (tcsetattr(fileno(stdin), TCSANOW, &oflags) != 0)
BOOST_THROW_EXCEPTION(ExternalFunctionFailure("tcsetattr"));
return password;
#endif #endif
} }

1
libdevcore/Exceptions.h

@ -54,6 +54,7 @@ struct FileError: virtual Exception {};
struct Overflow: virtual Exception {}; struct Overflow: virtual Exception {};
struct InterfaceNotSupported: virtual Exception { public: InterfaceNotSupported(std::string _f): Exception("Interface " + _f + " not supported.") {} }; struct InterfaceNotSupported: virtual Exception { public: InterfaceNotSupported(std::string _f): Exception("Interface " + _f + " not supported.") {} };
struct FailedInvariant: virtual Exception {}; struct FailedInvariant: virtual Exception {};
struct ExternalFunctionFailure: virtual Exception { public: ExternalFunctionFailure(std::string _f): Exception("Function " + _f + "() failed.") {} };
// error information to be added to exceptions // error information to be added to exceptions
using errinfo_invalidSymbol = boost::error_info<struct tag_invalidSymbol, char>; using errinfo_invalidSymbol = boost::error_info<struct tag_invalidSymbol, char>;

21
libdevcrypto/FileSystem.cpp

@ -25,8 +25,13 @@
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include <libdevcore/Log.h> #include <libdevcore/Log.h>
#ifdef _WIN32 #if defined(_WIN32)
#include <shlobj.h> #include <shlobj.h>
#elif defined(__APPLE__)
#include <stdlib.h>
#include <stdio.h>
#include <pwd.h>
#include <unistd.h>
#endif #endif
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
using namespace std; using namespace std;
@ -51,16 +56,20 @@ std::string dev::getDataDir(std::string _prefix)
#else #else
boost::filesystem::path dataDirPath; boost::filesystem::path dataDirPath;
char const* homeDir = getenv("HOME"); char const* homeDir = getenv("HOME");
#if defined(__APPLE__)
if (!homeDir || strlen(homeDir) == 0)
{
struct passwd* pwd = getpwuid(getuid());
if (pwd)
homeDir = pwd->pw_dir;
}
#endif
if (!homeDir || strlen(homeDir) == 0) if (!homeDir || strlen(homeDir) == 0)
dataDirPath = boost::filesystem::path("/"); dataDirPath = boost::filesystem::path("/");
else else
dataDirPath = boost::filesystem::path(homeDir); dataDirPath = boost::filesystem::path(homeDir);
#if defined(__APPLE__) && defined(__MACH__)
// This eventually needs to be put in proper wrapper (to support sandboxing)
return (dataDirPath / "Library/Application Support/Ethereum").string();
#else
return (dataDirPath / ("." + _prefix)).string(); return (dataDirPath / ("." + _prefix)).string();
#endif #endif
#endif
} }

29
libdevcrypto/OverlayDB.cpp

@ -38,6 +38,12 @@ OverlayDB::~OverlayDB()
cnote << "Closing state DB"; cnote << "Closing state DB";
} }
class WriteBatchNoter: public ldb::WriteBatch::Handler
{
virtual void Put(ldb::Slice const& _key, ldb::Slice const& _value) { cnote << "Put" << toHex(bytesConstRef(_key)) << "=>" << toHex(bytesConstRef(_value)); }
virtual void Delete(ldb::Slice const& _key) { cnote << "Delete" << toHex(bytesConstRef(_key)); }
};
void OverlayDB::commit() void OverlayDB::commit()
{ {
if (m_db) if (m_db)
@ -61,23 +67,34 @@ void OverlayDB::commit()
} }
} }
while (true) for (unsigned i = 0; i < 10; ++i)
{ {
ldb::Status o = m_db->Write(m_writeOptions, &batch); ldb::Status o = m_db->Write(m_writeOptions, &batch);
if (o.ok()) if (o.ok())
break; break;
cwarn << "Error writing to database. Sleeping a while then retrying. If it keeps saying this, free up some space!"; if (i == 9)
this_thread::sleep_for(chrono::milliseconds(500)); {
cwarn << "Fail writing to state database. Bombing out.";
exit(-1);
}
cwarn << "Error writing to state database: " << o.ToString();
WriteBatchNoter n;
batch.Iterate(&n);
cwarn << "Sleeping for" << (i + 1) << "seconds, then retrying.";
this_thread::sleep_for(chrono::seconds(i + 1));
}
DEV_WRITE_GUARDED(x_this)
{
m_aux.clear();
m_main.clear();
} }
m_aux.clear();
m_main.clear();
} }
} }
bytes OverlayDB::lookupAux(h256 const& _h) const bytes OverlayDB::lookupAux(h256 const& _h) const
{ {
bytes ret = MemoryDB::lookupAux(_h); bytes ret = MemoryDB::lookupAux(_h);
if (!ret.empty()) if (!ret.empty() || !m_db)
return move(ret); return move(ret);
std::string v; std::string v;
bytes b = _h.asBytes(); bytes b = _h.asBytes();

93
libdevcrypto/TrieDB.h

@ -220,6 +220,7 @@ public:
bool operator!=(Node const& _c) const { return !operator==(_c); } bool operator!=(Node const& _c) const { return !operator==(_c); }
}; };
protected:
std::vector<Node> m_trail; std::vector<Node> m_trail;
GenericTrieDB<DB> const* m_that; GenericTrieDB<DB> const* m_that;
}; };
@ -239,6 +240,7 @@ private:
void mergeAtAux(RLPStream& _out, RLP const& _replace, NibbleSlice _key, bytesConstRef _value); void mergeAtAux(RLPStream& _out, RLP const& _replace, NibbleSlice _key, bytesConstRef _value);
bytes mergeAt(RLP const& _replace, NibbleSlice _k, bytesConstRef _v, bool _inLine = false); bytes mergeAt(RLP const& _replace, NibbleSlice _k, bytesConstRef _v, bool _inLine = false);
bytes mergeAt(RLP const& _replace, h256 const& _replaceHash, NibbleSlice _k, bytesConstRef _v, bool _inLine = false);
bool deleteAtAux(RLPStream& _out, RLP const& _replace, NibbleSlice _key); bool deleteAtAux(RLPStream& _out, RLP const& _replace, NibbleSlice _key);
bytes deleteAt(RLP const& _replace, NibbleSlice _k); bytes deleteAt(RLP const& _replace, NibbleSlice _k);
@ -295,6 +297,7 @@ private:
// for the special case of the root (which is always looked up via a hash). In that case, // for the special case of the root (which is always looked up via a hash). In that case,
// use forceKillNode(). // use forceKillNode().
void killNode(RLP const& _d) { if (_d.data().size() >= 32) forceKillNode(sha3(_d.data())); } void killNode(RLP const& _d) { if (_d.data().size() >= 32) forceKillNode(sha3(_d.data())); }
void killNode(RLP const& _d, h256 const& _h) { if (_d.data().size() >= 32) forceKillNode(_h); }
h256 m_root; h256 m_root;
DB* m_db = nullptr; DB* m_db = nullptr;
@ -409,46 +412,59 @@ public:
iterator lower_bound(bytesConstRef) const { return iterator(); } iterator lower_bound(bytesConstRef) const { return iterator(); }
}; };
// Hashed & Basic // Hashed & Hash-key mapping
template <class DB> template <class _DB>
class FatGenericTrieDB: public GenericTrieDB<DB> class FatGenericTrieDB: private SpecificTrieDB<GenericTrieDB<_DB>, h256>
{ {
using Super = GenericTrieDB<DB>; using Super = SpecificTrieDB<GenericTrieDB<_DB>, h256>;
public: public:
FatGenericTrieDB(DB* _db): Super(_db), m_secure(_db) {} using DB = _DB;
FatGenericTrieDB(DB* _db, h256 _root, Verification _v = Verification::Normal) { open(_db, _root, _v); } FatGenericTrieDB(DB* _db = nullptr): Super(_db) {}
FatGenericTrieDB(DB* _db, h256 _root, Verification _v = Verification::Normal): Super(_db, _root, _v) {}
void open(DB* _db, h256 _root, Verification _v = Verification::Normal) { Super::open(_db); m_secure.open(_db); setRoot(_root, _v); }
void init() { Super::init(); m_secure.init(); syncRoot(); } using Super::init;
using Super::isNull;
using Super::isEmpty;
using Super::root;
using Super::leftOvers;
using Super::check;
using Super::open;
using Super::setRoot;
void setRoot(h256 _root, Verification _v = Verification::Normal) std::string at(bytesConstRef _key) const { return Super::at(sha3(_key)); }
bool contains(bytesConstRef _key) { return Super::contains(sha3(_key)); }
void insert(bytesConstRef _key, bytesConstRef _value)
{ {
if (!m_secure.isNull()) h256 hash = sha3(_key);
Super::db()->removeAux(m_secure.root()); Super::insert(hash, _value);
m_secure.setRoot(_root, _v); Super::db()->insertAux(hash, _key);
auto rb = Super::db()->lookupAux(m_secure.root());
auto r = h256(rb);
Super::setRoot(r, _v);
} }
h256 root() const { return m_secure.root(); } void remove(bytesConstRef _key) { Super::remove(sha3(_key)); }
void insert(bytesConstRef _key, bytesConstRef _value) { Super::insert(_key, _value); m_secure.insert(_key, _value); syncRoot(); }
void remove(bytesConstRef _key) { Super::remove(_key); m_secure.remove(_key); syncRoot(); }
h256Hash leftOvers(std::ostream* = nullptr) const { return h256Hash{}; } //friend class iterator;
bool check(bool) const { return m_secure.check(false) && Super::check(false); }
private: class iterator : public GenericTrieDB<_DB>::iterator
void syncRoot()
{ {
// Root changed. Need to record the mapping so we can determine on setRoot. public:
Super::db()->insertAux(m_secure.root(), Super::root().ref()); using Super = typename GenericTrieDB<_DB>::iterator;
}
HashedGenericTrieDB<DB> m_secure; iterator() { }
iterator(FatGenericTrieDB const* _trie): Super(_trie) { }
typename Super::value_type at() const
{
auto hashed = Super::at();
m_key = static_cast<FatGenericTrieDB const*>(Super::m_that)->db()->lookupAux(h256(hashed.first));
return std::make_pair(&m_key, std::move(hashed.second));
}
private:
mutable bytes m_key;
};
iterator begin() const { return iterator(); }
iterator end() const { return iterator(); }
}; };
template <class KeyType, class DB> using TrieDB = SpecificTrieDB<GenericTrieDB<DB>, KeyType>; template <class KeyType, class DB> using TrieDB = SpecificTrieDB<GenericTrieDB<DB>, KeyType>;
@ -745,7 +761,7 @@ template <class DB> void GenericTrieDB<DB>::insert(bytesConstRef _key, bytesCons
std::string rv = node(m_root); std::string rv = node(m_root);
assert(rv.size()); assert(rv.size());
bytes b = mergeAt(RLP(rv), NibbleSlice(_key), _value); bytes b = mergeAt(RLP(rv), m_root, NibbleSlice(_key), _value);
// mergeAt won't attempt to delete the node if it's less than 32 bytes // mergeAt won't attempt to delete the node if it's less than 32 bytes
// However, we know it's the root node and thus always hashed. // However, we know it's the root node and thus always hashed.
@ -765,8 +781,9 @@ template <class DB> std::string GenericTrieDB<DB>::atAux(RLP const& _here, Nibbl
if (_here.isEmpty() || _here.isNull()) if (_here.isEmpty() || _here.isNull())
// not found. // not found.
return std::string(); return std::string();
assert(_here.isList() && (_here.itemCount() == 2 || _here.itemCount() == 17)); unsigned itemCount = _here.itemCount();
if (_here.itemCount() == 2) assert(_here.isList() && (itemCount == 2 || itemCount == 17));
if (itemCount == 2)
{ {
auto k = keyOf(_here); auto k = keyOf(_here);
if (_key == k && isLeaf(_here)) if (_key == k && isLeaf(_here))
@ -792,6 +809,11 @@ template <class DB> std::string GenericTrieDB<DB>::atAux(RLP const& _here, Nibbl
} }
template <class DB> bytes GenericTrieDB<DB>::mergeAt(RLP const& _orig, NibbleSlice _k, bytesConstRef _v, bool _inLine) template <class DB> bytes GenericTrieDB<DB>::mergeAt(RLP const& _orig, NibbleSlice _k, bytesConstRef _v, bool _inLine)
{
return mergeAt(_orig, sha3(_orig.data()), _k, _v, _inLine);
}
template <class DB> bytes GenericTrieDB<DB>::mergeAt(RLP const& _orig, h256 const& _origHash, NibbleSlice _k, bytesConstRef _v, bool _inLine)
{ {
#if ETH_PARANOIA #if ETH_PARANOIA
tdebug << "mergeAt " << _orig << _k << sha3(_orig.data()); tdebug << "mergeAt " << _orig << _k << sha3(_orig.data());
@ -805,8 +827,9 @@ template <class DB> bytes GenericTrieDB<DB>::mergeAt(RLP const& _orig, NibbleSli
if (_orig.isEmpty()) if (_orig.isEmpty())
return place(_orig, _k, _v); return place(_orig, _k, _v);
assert(_orig.isList() && (_orig.itemCount() == 2 || _orig.itemCount() == 17)); unsigned itemCount = _orig.itemCount();
if (_orig.itemCount() == 2) assert(_orig.isList() && (itemCount == 2 || itemCount == 17));
if (itemCount == 2)
{ {
// pair... // pair...
NibbleSlice k = keyOf(_orig); NibbleSlice k = keyOf(_orig);
@ -819,7 +842,7 @@ template <class DB> bytes GenericTrieDB<DB>::mergeAt(RLP const& _orig, NibbleSli
if (_k.contains(k) && !isLeaf(_orig)) if (_k.contains(k) && !isLeaf(_orig))
{ {
if (!_inLine) if (!_inLine)
killNode(_orig); killNode(_orig, _origHash);
RLPStream s(2); RLPStream s(2);
s.append(_orig[0]); s.append(_orig[0]);
mergeAtAux(s, _orig[1], _k.mid(k.size()), _v); mergeAtAux(s, _orig[1], _k.mid(k.size()), _v);
@ -851,7 +874,7 @@ template <class DB> bytes GenericTrieDB<DB>::mergeAt(RLP const& _orig, NibbleSli
// Kill the node. // Kill the node.
if (!_inLine) if (!_inLine)
killNode(_orig); killNode(_orig, _origHash);
// not exactly our node - delve to next level at the correct index. // not exactly our node - delve to next level at the correct index.
byte n = _k[0]; byte n = _k[0];

33
test/libdevcrypto/TrieHash.cpp → libdevcrypto/TrieHash.cpp

@ -20,8 +20,8 @@
*/ */
#include "TrieHash.h" #include "TrieHash.h"
#include <libdevcrypto/TrieCommon.h> #include <libdevcrypto/TrieCommon.h>
#include <libdevcrypto/TrieDB.h> // @TODO replace ASAP!
#include <libdevcrypto/SHA3.h> #include <libdevcrypto/SHA3.h>
#include <libethcore/Common.h> #include <libethcore/Common.h>
using namespace std; using namespace std;
@ -197,4 +197,35 @@ h256 hash256(u256Map const& _s)
return sha3(s.out()); return sha3(s.out());
} }
/*h256 orderedTrieRoot(std::vector<bytes> const& _data)
{
StringMap m;
unsigned j = 0;
for (auto i: _data)
m[asString(rlp(j++))] = asString(i);
return hash256(m);
}*/
h256 orderedTrieRoot(std::vector<bytesConstRef> const& _data)
{
MemoryDB db;
GenericTrieDB<MemoryDB> t(&db);
t.init();
unsigned j = 0;
for (auto i: _data)
t.insert(rlp(j++), i.toBytes());
return t.root();
}
h256 orderedTrieRoot(std::vector<bytes> const& _data)
{
MemoryDB db;
GenericTrieDB<MemoryDB> t(&db);
t.init();
unsigned j = 0;
for (auto i: _data)
t.insert(rlp(j++), i);
return t.root();
}
} }

15
test/libdevcrypto/TrieHash.h → libdevcrypto/TrieHash.h

@ -31,4 +31,19 @@ bytes rlp256(StringMap const& _s);
h256 hash256(StringMap const& _s); h256 hash256(StringMap const& _s);
h256 hash256(u256Map const& _s); h256 hash256(u256Map const& _s);
/*h256 orderedTrieRoot(std::vector<bytes> const& _data);
template <class T, class U> inline h256 trieRootOver(unsigned _itemCount, T const& _getKey, U const& _getValue)
{
StringMap m;
for (unsigned i = 0; i < _itemCount; ++i)
m[asString(_getKey(i))] = asString(_getValue(i));
return hash256(m);
}*/
using bytesMap = std::unordered_map<bytes, bytes>;
h256 orderedTrieRoot(std::vector<bytesConstRef> const& _data);
h256 orderedTrieRoot(std::vector<bytes> const& _data);
} }

5
libethash-cl/ethash_cl_miner.cpp

@ -61,6 +61,11 @@ ethash_cl_miner::ethash_cl_miner()
{ {
} }
ethash_cl_miner::~ethash_cl_miner()
{
finish();
}
std::string ethash_cl_miner::platform_info(unsigned _platformId, unsigned _deviceId) std::string ethash_cl_miner::platform_info(unsigned _platformId, unsigned _deviceId)
{ {
std::vector<cl::Platform> platforms; std::vector<cl::Platform> platforms;

1
libethash-cl/ethash_cl_miner.h

@ -30,6 +30,7 @@ public:
public: public:
ethash_cl_miner(); ethash_cl_miner();
~ethash_cl_miner();
static unsigned get_num_platforms(); static unsigned get_num_platforms();
static unsigned get_num_devices(unsigned _platformId = 0); static unsigned get_num_devices(unsigned _platformId = 0);

4
libethash/CMakeLists.txt

@ -43,6 +43,4 @@ if (CRYPTOPP_FOUND)
TARGET_LINK_LIBRARIES(${LIBRARY} ${CRYPTOPP_LIBRARIES}) TARGET_LINK_LIBRARIES(${LIBRARY} ${CRYPTOPP_LIBRARIES})
endif() endif()
if (NOT ETHASHCL) install( TARGETS ${LIBRARY} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib )
install( TARGETS ${LIBRARY} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib )
endif ()

9
libethash/io_posix.c

@ -26,6 +26,8 @@
#include <libgen.h> #include <libgen.h>
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
#include <stdlib.h>
#include <pwd.h>
FILE* ethash_fopen(char const* file_name, char const* mode) FILE* ethash_fopen(char const* file_name, char const* mode)
{ {
@ -89,6 +91,13 @@ bool ethash_get_default_dirname(char* strbuf, size_t buffsize)
static const char dir_suffix[] = ".ethash/"; static const char dir_suffix[] = ".ethash/";
strbuf[0] = '\0'; strbuf[0] = '\0';
char* home_dir = getenv("HOME"); char* home_dir = getenv("HOME");
if (!home_dir || strlen(home_dir) == 0)
{
struct passwd* pwd = getpwuid(getuid());
if (pwd)
home_dir = pwd->pw_dir;
}
size_t len = strlen(home_dir); size_t len = strlen(home_dir);
if (!ethash_strncat(strbuf, buffsize, home_dir, len)) { if (!ethash_strncat(strbuf, buffsize, home_dir, len)) {
return false; return false;

4
libethash/io_win32.c

@ -87,9 +87,9 @@ bool ethash_file_size(FILE* f, size_t* ret_size)
bool ethash_get_default_dirname(char* strbuf, size_t buffsize) bool ethash_get_default_dirname(char* strbuf, size_t buffsize)
{ {
static const char dir_suffix[] = "Appdata\\Ethash\\"; static const char dir_suffix[] = "Ethash\\";
strbuf[0] = '\0'; strbuf[0] = '\0';
if (!SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_PROFILE, NULL, 0, (WCHAR*)strbuf))) { if (!SUCCEEDED(SHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, (CHAR*)strbuf))) {
return false; return false;
} }
if (!ethash_strncat(strbuf, buffsize, "\\", 1)) { if (!ethash_strncat(strbuf, buffsize, "\\", 1)) {

39
libethcore/BlockInfo.cpp

@ -22,6 +22,7 @@
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include <libdevcore/RLP.h> #include <libdevcore/RLP.h>
#include <libdevcrypto/TrieDB.h> #include <libdevcrypto/TrieDB.h>
#include <libdevcrypto/TrieHash.h>
#include <libethcore/Common.h> #include <libethcore/Common.h>
#include <libethcore/Params.h> #include <libethcore/Params.h>
#include "EthashAux.h" #include "EthashAux.h"
@ -173,6 +174,9 @@ void BlockInfo::populateFromHeader(RLP const& _header, Strictness _s, h256 const
void BlockInfo::populate(bytesConstRef _block, Strictness _s, h256 const& _h) void BlockInfo::populate(bytesConstRef _block, Strictness _s, h256 const& _h)
{ {
RLP root(_block); RLP root(_block);
if (!root.isList())
BOOST_THROW_EXCEPTION(InvalidBlockFormat() << errinfo_comment("block needs to be a list") << BadFieldError(0, _block.toString()));
RLP header = root[0]; RLP header = root[0];
if (!header.isList()) if (!header.isList())
@ -185,6 +189,8 @@ void BlockInfo::populate(bytesConstRef _block, Strictness _s, h256 const& _h)
BOOST_THROW_EXCEPTION(InvalidBlockFormat() << errinfo_comment("block uncles need to be a list") << BadFieldError(2, root[2].data().toString())); BOOST_THROW_EXCEPTION(InvalidBlockFormat() << errinfo_comment("block uncles need to be a list") << BadFieldError(2, root[2].data().toString()));
} }
struct BlockInfoDiagnosticsChannel: public LogChannel { static const char* name() { return EthBlue "" EthWhite ""; } static const int verbosity = 9; };
template <class T, class U> h256 trieRootOver(unsigned _itemCount, T const& _getKey, U const& _getValue) template <class T, class U> h256 trieRootOver(unsigned _itemCount, T const& _getKey, U const& _getValue)
{ {
MemoryDB db; MemoryDB db;
@ -195,17 +201,41 @@ template <class T, class U> h256 trieRootOver(unsigned _itemCount, T const& _get
return t.root(); return t.root();
} }
struct BlockInfoDiagnosticsChannel: public LogChannel { static const char* name() { return EthBlue "" EthWhite ""; } static const int verbosity = 9; };
void BlockInfo::verifyInternals(bytesConstRef _block) const void BlockInfo::verifyInternals(bytesConstRef _block) const
{ {
RLP root(_block); RLP root(_block);
auto txList = root[1]; auto txList = root[1];
auto expectedRoot = trieRootOver(txList.itemCount(), [&](unsigned i){ return rlp(i); }, [&](unsigned i){ return txList[i].data(); }); auto expectedRoot = trieRootOver(txList.itemCount(), [&](unsigned i){ return rlp(i); }, [&](unsigned i){ return txList[i].data(); });
clog(BlockInfoDiagnosticsChannel) << "Expected trie root:" << toString(expectedRoot); clog(BlockInfoDiagnosticsChannel) << "Expected trie root:" << toString(expectedRoot);
if (transactionsRoot != expectedRoot) if (transactionsRoot != expectedRoot)
{
MemoryDB tm;
GenericTrieDB<MemoryDB> transactionsTrie(&tm);
transactionsTrie.init();
vector<bytesConstRef> txs;
for (unsigned i = 0; i < txList.itemCount(); ++i)
{
RLPStream k;
k << i;
transactionsTrie.insert(&k.out(), txList[i].data());
txs.push_back(txList[i].data());
cdebug << toHex(k.out()) << toHex(txList[i].data());
}
cdebug << "trieRootOver" << expectedRoot;
cdebug << "orderedTrieRoot" << orderedTrieRoot(txs);
cdebug << "TrieDB" << transactionsTrie.root();
cdebug << "Contents:";
for (auto const& t: txs)
cdebug << toHex(t);
BOOST_THROW_EXCEPTION(InvalidTransactionsHash() << HashMismatchError(expectedRoot, transactionsRoot)); BOOST_THROW_EXCEPTION(InvalidTransactionsHash() << HashMismatchError(expectedRoot, transactionsRoot));
}
clog(BlockInfoDiagnosticsChannel) << "Expected uncle hash:" << toString(sha3(root[2].data())); clog(BlockInfoDiagnosticsChannel) << "Expected uncle hash:" << toString(sha3(root[2].data()));
if (sha3Uncles != sha3(root[2].data())) if (sha3Uncles != sha3(root[2].data()))
BOOST_THROW_EXCEPTION(InvalidUnclesHash()); BOOST_THROW_EXCEPTION(InvalidUnclesHash());
@ -228,7 +258,10 @@ u256 BlockInfo::selectGasLimit(BlockInfo const& _parent) const
return c_genesisGasLimit; return c_genesisGasLimit;
else else
// target minimum of 3141592 // target minimum of 3141592
return max<u256>(max<u256>(c_minGasLimit, 3141592), _parent.gasLimit - _parent.gasLimit / c_gasLimitBoundDivisor + 1 + (_parent.gasUsed * 6 / 5) / c_gasLimitBoundDivisor); if (_parent.gasLimit < c_genesisGasLimit)
return min<u256>(c_genesisGasLimit, _parent.gasLimit + _parent.gasLimit / c_gasLimitBoundDivisor - 1);
else
return max<u256>(c_genesisGasLimit, _parent.gasLimit - _parent.gasLimit / c_gasLimitBoundDivisor + 1 + (_parent.gasUsed * 6 / 5) / c_gasLimitBoundDivisor);
} }
u256 BlockInfo::calculateDifficulty(BlockInfo const& _parent) const u256 BlockInfo::calculateDifficulty(BlockInfo const& _parent) const

2
libethcore/CMakeLists.txt

@ -28,7 +28,7 @@ add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS})
target_link_libraries(${EXECUTABLE} ethash) target_link_libraries(${EXECUTABLE} ethash)
target_link_libraries(${EXECUTABLE} devcrypto) target_link_libraries(${EXECUTABLE} devcrypto)
target_link_libraries(${EXECUTABLE} evmcore) #target_link_libraries(${EXECUTABLE} evmcore)
if (ETHASHCL) if (ETHASHCL)
target_link_libraries(${EXECUTABLE} ethash-cl) target_link_libraries(${EXECUTABLE} ethash-cl)

6
libethcore/Common.cpp

@ -36,11 +36,13 @@ namespace eth
{ {
const unsigned c_protocolVersion = 60; const unsigned c_protocolVersion = 60;
const unsigned c_minorProtocolVersion = 2;
const unsigned c_databaseBaseVersion = 9;
#if ETH_FATDB #if ETH_FATDB
const unsigned c_minorProtocolVersion = 3;
const unsigned c_databaseBaseVersion = 9;
const unsigned c_databaseVersionModifier = 1; const unsigned c_databaseVersionModifier = 1;
#else #else
const unsigned c_minorProtocolVersion = 2;
const unsigned c_databaseBaseVersion = 9;
const unsigned c_databaseVersionModifier = 0; const unsigned c_databaseVersionModifier = 0;
#endif #endif

5
libethcore/Common.h

@ -100,9 +100,10 @@ struct ImportRequirements
using value = unsigned; using value = unsigned;
enum enum
{ {
ValidNonce = 1, ///< Validate Nonce ValidNonce = 1, ///< Validate nonce
DontHave = 2, ///< Avoid old blocks DontHave = 2, ///< Avoid old blocks
Default = ValidNonce | DontHave CheckUncles = 4, ///< Check uncle nonces
Default = ValidNonce | DontHave | CheckUncles
}; };
}; };

31
libethcore/Ethash.cpp

@ -72,13 +72,12 @@ Ethash::WorkPackage Ethash::package(BlockInfo const& _bi)
ret.boundary = _bi.boundary(); ret.boundary = _bi.boundary();
ret.headerHash = _bi.headerHash(WithoutNonce); ret.headerHash = _bi.headerHash(WithoutNonce);
ret.seedHash = _bi.seedHash(); ret.seedHash = _bi.seedHash();
ret.blockNumber = (uint64_t) _bi.number;
return ret; return ret;
} }
void Ethash::prep(BlockInfo const& _header, std::function<int(unsigned)> const& _f) void Ethash::prep(BlockInfo const& _header, std::function<int(unsigned)> const& _f)
{ {
EthashAux::full((unsigned)_header.number, _f); EthashAux::full(_header.seedHash(), true, _f);
} }
bool Ethash::preVerify(BlockInfo const& _header) bool Ethash::preVerify(BlockInfo const& _header)
@ -135,7 +134,10 @@ void Ethash::CPUMiner::workLoop()
WorkPackage w = work(); WorkPackage w = work();
auto dag = EthashAux::full(w.blockNumber); EthashAux::FullType dag;
while (!shouldStop() && !(dag = EthashAux::full(w.seedHash)))
this_thread::sleep_for(chrono::milliseconds(500));
h256 boundary = w.boundary; h256 boundary = w.boundary;
unsigned hashCount = 1; unsigned hashCount = 1;
for (; !shouldStop(); tryNonce++, hashCount++) for (; !shouldStop(); tryNonce++, hashCount++)
@ -144,8 +146,8 @@ void Ethash::CPUMiner::workLoop()
h256 value = h256((uint8_t*)&ethashReturn.result, h256::ConstructFromPointer); h256 value = h256((uint8_t*)&ethashReturn.result, h256::ConstructFromPointer);
if (value <= boundary && submitProof(Solution{(Nonce)(u64)tryNonce, h256((uint8_t*)&ethashReturn.mix_hash, h256::ConstructFromPointer)})) if (value <= boundary && submitProof(Solution{(Nonce)(u64)tryNonce, h256((uint8_t*)&ethashReturn.mix_hash, h256::ConstructFromPointer)}))
break; break;
if (!(hashCount % 1000)) if (!(hashCount % 100))
accumulateHashes(1000); accumulateHashes(100);
} }
} }
@ -284,7 +286,7 @@ Ethash::GPUMiner::~GPUMiner()
bool Ethash::GPUMiner::report(uint64_t _nonce) bool Ethash::GPUMiner::report(uint64_t _nonce)
{ {
Nonce n = (Nonce)(u64)_nonce; Nonce n = (Nonce)(u64)_nonce;
Result r = EthashAux::eval(work().blockNumber, work().headerHash, n); Result r = EthashAux::eval(work().seedHash, work().headerHash, n);
if (r.value < work().boundary) if (r.value < work().boundary)
return submitProof(Solution{n, r.mixHash}); return submitProof(Solution{n, r.mixHash});
return false; return false;
@ -301,6 +303,7 @@ void Ethash::GPUMiner::workLoop()
// take local copy of work since it may end up being overwritten by kickOff/pause. // take local copy of work since it may end up being overwritten by kickOff/pause.
try { try {
WorkPackage w = work(); WorkPackage w = work();
cnote << "workLoop" << !!m_miner << m_minerSeed << w.seedHash;
if (!m_miner || m_minerSeed != w.seedHash) if (!m_miner || m_minerSeed != w.seedHash)
{ {
m_minerSeed = w.seedHash; m_minerSeed = w.seedHash;
@ -310,9 +313,19 @@ void Ethash::GPUMiner::workLoop()
unsigned device = instances() > 1 ? index() : s_deviceId; unsigned device = instances() > 1 ? index() : s_deviceId;
if (!EthashAux::computeFull(w.blockNumber)) EthashAux::FullType dag;
return; while (true)
EthashAux::FullType dag = EthashAux::full(w.blockNumber); {
if ((dag = EthashAux::full(w.seedHash, false)))
break;
if (shouldStop())
{
delete m_miner;
return;
}
cnote << "Awaiting DAG";
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); m_miner->init(dagData.data(), dagData.size(), 32, s_platformId, device);
} }

6
libethcore/Ethash.h

@ -66,8 +66,7 @@ public:
h256 boundary; h256 boundary;
h256 headerHash; ///< When h256() means "pause until notified a new work package is available". h256 headerHash; ///< When h256() means "pause until notified a new work package is available".
h256 seedHash; /// LTODO: IS this needed now that we use the block number instead? h256 seedHash;
uint64_t blockNumber;
}; };
static const WorkPackage NullWorkPackage; static const WorkPackage NullWorkPackage;
@ -79,7 +78,6 @@ public:
static bool preVerify(BlockInfo const& _header); static bool preVerify(BlockInfo const& _header);
static WorkPackage package(BlockInfo const& _header); static WorkPackage package(BlockInfo const& _header);
static void assignResult(Solution const& _r, BlockInfo& _header) { _header.nonce = _r.nonce; _header.mixHash = _r.mixHash; } static void assignResult(Solution const& _r, BlockInfo& _header) { _header.nonce = _r.nonce; _header.mixHash = _r.mixHash; }
class CPUMiner: public Miner, Worker class CPUMiner: public Miner, Worker
{ {
@ -120,7 +118,7 @@ public:
static void setDefaultPlatform(unsigned _id) { s_platformId = _id; } static void setDefaultPlatform(unsigned _id) { s_platformId = _id; }
static void setDefaultDevice(unsigned _id) { s_deviceId = _id; } static void setDefaultDevice(unsigned _id) { s_deviceId = _id; }
static void setNumInstances(unsigned _instances) { s_numInstances = std::min<unsigned>(_instances, getNumDevices()); } static void setNumInstances(unsigned _instances) { s_numInstances = std::min<unsigned>(_instances, getNumDevices()); }
protected: protected:
void kickOff() override; void kickOff() override;
void pause() override; void pause() override;

82
libethcore/EthashAux.cpp

@ -101,27 +101,24 @@ uint64_t EthashAux::number(h256 const& _seedHash)
void EthashAux::killCache(h256 const& _s) void EthashAux::killCache(h256 const& _s)
{ {
RecursiveGuard l(x_lights); WriteGuard l(x_lights);
m_lights.erase(_s); m_lights.erase(_s);
} }
EthashAux::LightType EthashAux::light(BlockInfo const& _header) EthashAux::LightType EthashAux::light(h256 const& _seedHash)
{ {
return light((uint64_t)_header.number); ReadGuard l(get()->x_lights);
LightType ret = get()->m_lights[_seedHash];
return ret ? ret : (get()->m_lights[_seedHash] = make_shared<LightAllocation>(_seedHash));
} }
EthashAux::LightType EthashAux::light(uint64_t _blockNumber) EthashAux::LightAllocation::LightAllocation(h256 const& _seedHash)
{ {
RecursiveGuard l(get()->x_lights); uint64_t blockNumber = EthashAux::number(_seedHash);
h256 seedHash = EthashAux::seedHash(_blockNumber); light = ethash_light_new(blockNumber);
LightType ret = get()->m_lights[seedHash]; if (!light)
return ret ? ret : (get()->m_lights[seedHash] = make_shared<LightAllocation>(_blockNumber)); BOOST_THROW_EXCEPTION(ExternalFunctionFailure("ethash_light_new()"));
} size = ethash_get_cachesize(blockNumber);
EthashAux::LightAllocation::LightAllocation(uint64_t _blockNumber)
{
light = ethash_light_new(_blockNumber);
size = ethash_get_cachesize(_blockNumber);
} }
EthashAux::LightAllocation::~LightAllocation() EthashAux::LightAllocation::~LightAllocation()
@ -137,6 +134,8 @@ bytesConstRef EthashAux::LightAllocation::data() const
EthashAux::FullAllocation::FullAllocation(ethash_light_t _light, ethash_callback_t _cb) EthashAux::FullAllocation::FullAllocation(ethash_light_t _light, ethash_callback_t _cb)
{ {
full = ethash_full_new(_light, _cb); full = ethash_full_new(_light, _cb);
if (!full)
BOOST_THROW_EXCEPTION(ExternalFunctionFailure("ethash_full_new()"));
} }
EthashAux::FullAllocation::~FullAllocation() EthashAux::FullAllocation::~FullAllocation()
@ -156,32 +155,45 @@ static int dagCallbackShim(unsigned _p)
return s_dagCallback ? s_dagCallback(_p) : 0; return s_dagCallback ? s_dagCallback(_p) : 0;
} }
EthashAux::FullType EthashAux::full(uint64_t _blockNumber, function<int(unsigned)> const& _f) EthashAux::FullType EthashAux::full(h256 const& _seedHash, bool _createIfMissing, function<int(unsigned)> const& _f)
{ {
auto l = light(_blockNumber);
h256 seedHash = EthashAux::seedHash(_blockNumber);
FullType ret; FullType ret;
auto l = light(_seedHash);
DEV_GUARDED(get()->x_fulls) DEV_GUARDED(get()->x_fulls)
if ((ret = get()->m_fulls[seedHash].lock())) if ((ret = get()->m_fulls[_seedHash].lock()))
{ {
get()->m_lastUsedFull = ret; get()->m_lastUsedFull = ret;
return ret; return ret;
} }
s_dagCallback = _f; if (_createIfMissing || computeFull(_seedHash) == 100)
ret = make_shared<FullAllocation>(l->light, dagCallbackShim); {
s_dagCallback = _f;
cnote << "Loading from libethash...";
ret = make_shared<FullAllocation>(l->light, dagCallbackShim);
cnote << "Done loading.";
DEV_GUARDED(get()->x_fulls)
get()->m_fulls[_seedHash] = get()->m_lastUsedFull = ret;
}
DEV_GUARDED(get()->x_fulls)
get()->m_fulls[seedHash] = get()->m_lastUsedFull = ret;
return ret; return ret;
} }
unsigned EthashAux::computeFull(uint64_t _blockNumber) #define DEV_IF_THROWS(X) try { X; } catch (...)
unsigned EthashAux::computeFull(h256 const& _seedHash)
{ {
Guard l(get()->x_fulls); Guard l(get()->x_fulls);
h256 seedHash = EthashAux::seedHash(_blockNumber); uint64_t blockNumber;
if (FullType ret = get()->m_fulls[seedHash].lock())
DEV_IF_THROWS(blockNumber = EthashAux::number(_seedHash))
{
return 0;
}
if (FullType ret = get()->m_fulls[_seedHash].lock())
{ {
get()->m_lastUsedFull = ret; get()->m_lastUsedFull = ret;
return 100; return 100;
@ -190,15 +202,17 @@ unsigned EthashAux::computeFull(uint64_t _blockNumber)
if (!get()->m_fullGenerator || !get()->m_fullGenerator->joinable()) if (!get()->m_fullGenerator || !get()->m_fullGenerator->joinable())
{ {
get()->m_fullProgress = 0; get()->m_fullProgress = 0;
get()->m_generatingFullNumber = _blockNumber / ETHASH_EPOCH_LENGTH * ETHASH_EPOCH_LENGTH; get()->m_generatingFullNumber = blockNumber / ETHASH_EPOCH_LENGTH * ETHASH_EPOCH_LENGTH;
get()->m_fullGenerator = unique_ptr<thread>(new thread([=](){ get()->m_fullGenerator = unique_ptr<thread>(new thread([=](){
get()->full(_blockNumber, [](unsigned p){ get()->m_fullProgress = p; return 0; }); cnote << "Loading full DAG of seedhash: " << _seedHash;
get()->full(_seedHash, true, [](unsigned p){ get()->m_fullProgress = p; return 0; });
cnote << "Full DAG loaded";
get()->m_fullProgress = 0; get()->m_fullProgress = 0;
get()->m_generatingFullNumber = NotGenerating; get()->m_generatingFullNumber = NotGenerating;
})); }));
} }
return (get()->m_generatingFullNumber == _blockNumber) ? get()->m_fullProgress : 0; return (get()->m_generatingFullNumber == blockNumber) ? get()->m_fullProgress : 0;
} }
Ethash::Result EthashAux::FullAllocation::compute(h256 const& _headerHash, Nonce const& _nonce) const Ethash::Result EthashAux::FullAllocation::compute(h256 const& _headerHash, Nonce const& _nonce) const
@ -219,13 +233,15 @@ Ethash::Result EthashAux::LightAllocation::compute(h256 const& _headerHash, Nonc
Ethash::Result EthashAux::eval(BlockInfo const& _header, Nonce const& _nonce) Ethash::Result EthashAux::eval(BlockInfo const& _header, Nonce const& _nonce)
{ {
return eval((uint64_t)_header.number, _header.headerHash(WithoutNonce), _nonce); return eval(_header.seedHash(), _header.headerHash(WithoutNonce), _nonce);
} }
Ethash::Result EthashAux::eval(uint64_t _blockNumber, h256 const& _headerHash, Nonce const& _nonce) Ethash::Result EthashAux::eval(h256 const& _seedHash, h256 const& _headerHash, Nonce const& _nonce)
{ {
h256 seedHash = EthashAux::seedHash(_blockNumber); if (FullType dag = get()->m_fulls[_seedHash].lock())
if (FullType dag = get()->m_fulls[seedHash].lock())
return dag->compute(_headerHash, _nonce); return dag->compute(_headerHash, _nonce);
return EthashAux::get()->light(_blockNumber)->compute(_headerHash, _nonce); DEV_IF_THROWS(return EthashAux::get()->light(_seedHash)->compute(_headerHash, _nonce))
{
return Ethash::Result{ ~h256(), h256() };
}
} }

21
libethcore/EthashAux.h

@ -19,6 +19,8 @@
* @date 2014 * @date 2014
*/ */
#pragma once
#include <condition_variable> #include <condition_variable>
#include <libethash/ethash.h> #include <libethash/ethash.h>
#include <libdevcore/Worker.h> #include <libdevcore/Worker.h>
@ -40,7 +42,7 @@ public:
struct LightAllocation struct LightAllocation
{ {
LightAllocation(uint64_t _blockNumber); LightAllocation(h256 const& _seedHash);
~LightAllocation(); ~LightAllocation();
bytesConstRef data() const; bytesConstRef data() const;
Ethash::Result compute(h256 const& _headerHash, Nonce const& _nonce) const; Ethash::Result compute(h256 const& _headerHash, Nonce const& _nonce) const;
@ -65,29 +67,30 @@ public:
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 LightType light(BlockInfo const& _header); static LightType light(h256 const& _seedHash);
static LightType light(uint64_t _blockNumber);
static const uint64_t NotGenerating = (uint64_t)-1; static const uint64_t NotGenerating = (uint64_t)-1;
/// Kicks off generation of DAG for @a _blocknumber and @returns false or @returns true if ready. /// Kicks off generation of DAG for @a _seedHash and @returns false or @returns true if ready.
static unsigned computeFull(uint64_t _blockNumber); static unsigned computeFull(h256 const& _seedHash);
/// Information on the generation progress. /// Information on the generation progress.
static std::pair<uint64_t, unsigned> fullGeneratingProgress() { return std::make_pair(get()->m_generatingFullNumber, get()->m_fullProgress); } static std::pair<uint64_t, unsigned> fullGeneratingProgress() { return std::make_pair(get()->m_generatingFullNumber, get()->m_fullProgress); }
/// Kicks off generation of DAG for @a _blocknumber and blocks until ready; @returns result. /// Kicks off generation of DAG for @a _blocknumber and blocks until ready; @returns result or empty pointer if not existing and _createIfMissing is false.
static FullType full(uint64_t _blockNumber, std::function<int(unsigned)> const& _f = std::function<int(unsigned)>()); static FullType full(h256 const& _seedHash, bool _createIfMissing = false, std::function<int(unsigned)> const& _f = std::function<int(unsigned)>());
static Ethash::Result eval(BlockInfo const& _header) { return eval(_header, _header.nonce); } static Ethash::Result eval(BlockInfo const& _header) { return eval(_header, _header.nonce); }
static Ethash::Result eval(BlockInfo const& _header, Nonce const& _nonce); static Ethash::Result eval(BlockInfo const& _header, Nonce const& _nonce);
static Ethash::Result eval(uint64_t _blockNumber, h256 const& _headerHash, Nonce const& _nonce); static Ethash::Result eval(h256 const& _seedHash, h256 const& _headerHash, Nonce const& _nonce);
private: private:
EthashAux() {} EthashAux() {}
/// Kicks off generation of DAG for @a _blocknumber and blocks until ready; @returns result.
void killCache(h256 const& _s); void killCache(h256 const& _s);
static EthashAux* s_this; static EthashAux* s_this;
RecursiveMutex x_lights; SharedMutex x_lights;
std::unordered_map<h256, std::shared_ptr<LightAllocation>> m_lights; std::unordered_map<h256, std::shared_ptr<LightAllocation>> m_lights;
Mutex x_fulls; Mutex x_fulls;

0
libethereum/Farm.h → libethcore/Farm.h

139
libethereum/BlockChain.cpp

@ -199,7 +199,7 @@ void BlockChain::close()
#define IGNORE_EXCEPTIONS(X) try { X; } catch (...) {} #define IGNORE_EXCEPTIONS(X) try { X; } catch (...) {}
void BlockChain::rebuild(std::string const& _path, std::function<void(unsigned, unsigned)> const& _progress) void BlockChain::rebuild(std::string const& _path, std::function<void(unsigned, unsigned)> const& _progress, bool _prepPoW)
{ {
std::string path = _path.empty() ? Defaults::get()->m_dbPath : _path; std::string path = _path.empty() ? Defaults::get()->m_dbPath : _path;
@ -252,7 +252,8 @@ void BlockChain::rebuild(std::string const& _path, std::function<void(unsigned,
bytes b = block(queryExtras<BlockHash, ExtraBlockHash>(h256(u256(d)), m_blockHashes, x_blockHashes, NullBlockHash, oldExtrasDB).value); bytes b = block(queryExtras<BlockHash, ExtraBlockHash>(h256(u256(d)), m_blockHashes, x_blockHashes, NullBlockHash, oldExtrasDB).value);
BlockInfo bi(b); BlockInfo bi(b);
ProofOfWork::prep(bi); if (_prepPoW)
ProofOfWork::prep(bi);
if (bi.parentHash != lastHash) if (bi.parentHash != lastHash)
{ {
@ -306,7 +307,7 @@ tuple<h256s, h256s, bool> BlockChain::sync(BlockQueue& _bq, OverlayDB const& _st
{ {
// _bq.tick(*this); // _bq.tick(*this);
vector<bytes> blocks; vector<pair<BlockInfo, bytes>> blocks;
_bq.drain(blocks, _max); _bq.drain(blocks, _max);
h256s fresh; h256s fresh;
@ -316,7 +317,8 @@ tuple<h256s, h256s, bool> BlockChain::sync(BlockQueue& _bq, OverlayDB const& _st
{ {
try try
{ {
auto r = import(block, _stateDB); // Nonce & uncle nonces already verified thread at this point.
auto r = import(block.first, block.second, _stateDB, ImportRequirements::Default & ~ImportRequirements::ValidNonce & ~ImportRequirements::CheckUncles);
fresh += r.first; fresh += r.first;
dead += r.second; dead += r.second;
} }
@ -325,14 +327,14 @@ tuple<h256s, h256s, bool> BlockChain::sync(BlockQueue& _bq, OverlayDB const& _st
cwarn << "ODD: Import queue contains block with unknown parent." << LogTag::Error << boost::current_exception_diagnostic_information(); 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. // NOTE: don't reimport since the queue should guarantee everything in the right order.
// Can't continue - chain bad. // Can't continue - chain bad.
badBlocks.push_back(BlockInfo::headerHash(block)); badBlocks.push_back(block.first.hash());
} }
catch (Exception const& _e) catch (Exception const& _e)
{ {
cnote << "Exception while importing block. Someone (Jeff? That you?) seems to be giving us dodgy blocks!" << LogTag::Error << diagnostic_information(_e); cnote << "Exception while importing block. Someone (Jeff? That you?) seems to be giving us dodgy blocks!" << LogTag::Error << diagnostic_information(_e);
// NOTE: don't reimport since the queue should guarantee everything in the right order. // NOTE: don't reimport since the queue should guarantee everything in the right order.
// Can't continue - chain bad. // Can't continue - chain bad.
badBlocks.push_back(BlockInfo::headerHash(block)); badBlocks.push_back(block.first.hash());
} }
} }
return make_tuple(fresh, dead, _bq.doneDrain(badBlocks)); return make_tuple(fresh, dead, _bq.doneDrain(badBlocks));
@ -364,18 +366,6 @@ 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)
{ {
//@tidy This is a behemoth of a method - could do to be split into a few smaller ones.
#if ETH_TIMED_IMPORTS
boost::timer total;
double preliminaryChecks;
double enactment;
double collation;
double writing;
double checkBest;
boost::timer t;
#endif
// 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; BlockInfo bi;
@ -383,11 +373,6 @@ ImportRoute BlockChain::import(bytes const& _block, OverlayDB const& _db, Import
try try
#endif #endif
{ {
RLP blockRLP(_block);
if (!blockRLP.isList())
BOOST_THROW_EXCEPTION(InvalidBlockFormat() << errinfo_comment("block header needs to be a list") << BadFieldError(0, blockRLP.data().toString()));
bi.populate(&_block); bi.populate(&_block);
bi.verifyInternals(&_block); bi.verifyInternals(&_block);
} }
@ -400,29 +385,46 @@ ImportRoute BlockChain::import(bytes const& _block, OverlayDB const& _db, Import
} }
#endif #endif
return import(bi, _block, _db, _ir);
}
ImportRoute BlockChain::import(BlockInfo const& _bi, bytes 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.
#if ETH_TIMED_IMPORTS
boost::timer total;
double preliminaryChecks;
double enactment;
double collation;
double writing;
double checkBest;
boost::timer t;
#endif
// Check block doesn't already exist first! // Check block doesn't already exist first!
if (isKnown(bi.hash()) && (_ir & ImportRequirements::DontHave)) if (isKnown(_bi.hash()) && (_ir & ImportRequirements::DontHave))
{ {
clog(BlockChainNote) << bi.hash() << ": Not new."; clog(BlockChainNote) << _bi.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(_bi.parentHash))
{ {
clog(BlockChainNote) << bi.hash() << ": Unknown parent " << bi.parentHash; clog(BlockChainNote) << _bi.hash() << ": Unknown parent " << _bi.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(_bi.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(_bi.parentHash);
clog(BlockChainDebug) << "isKnown:" << isKnown(bi.parentHash); clog(BlockChainDebug) << "isKnown:" << isKnown(_bi.parentHash);
clog(BlockChainDebug) << "last/number:" << m_lastBlockNumber << m_lastBlockHash << bi.number; clog(BlockChainDebug) << "last/number:" << m_lastBlockNumber << m_lastBlockHash << _bi.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";
@ -430,14 +432,14 @@ ImportRoute BlockChain::import(bytes const& _block, OverlayDB const& _db, Import
} }
// Check it's not crazy // Check it's not crazy
if (bi.timestamp > (u256)time(0)) if (_bi.timestamp > (u256)time(0))
{ {
clog(BlockChainChat) << bi.hash() << ": Future time " << bi.timestamp << " (now at " << time(0) << ")"; clog(BlockChainChat) << _bi.hash() << ": Future time " << _bi.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 " << _bi.hash() << "...";
#if ETH_TIMED_IMPORTS #if ETH_TIMED_IMPORTS
preliminaryChecks = t.elapsed(); preliminaryChecks = t.elapsed();
@ -457,7 +459,7 @@ ImportRoute BlockChain::import(bytes const& _block, OverlayDB const& _db, Import
// 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, _bi, *this, _ir);
BlockLogBlooms blb; BlockLogBlooms blb;
BlockReceipts br; BlockReceipts br;
@ -493,22 +495,22 @@ ImportRoute BlockChain::import(bytes const& _block, OverlayDB const& _db, Import
// 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(_bi.parentHash);
DEV_WRITE_GUARDED(x_details) DEV_WRITE_GUARDED(x_details)
m_details[bi.parentHash].children.push_back(bi.hash()); m_details[_bi.parentHash].children.push_back(_bi.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(_bi.hash()), (ldb::Slice)ref(_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(_bi.parentHash, ExtraDetails), (ldb::Slice)dev::ref(m_details[_bi.parentHash].rlp()));
extrasBatch.Put(toSlice(bi.hash(), ExtraDetails), (ldb::Slice)dev::ref(BlockDetails((unsigned)pd.number + 1, td, bi.parentHash, {}).rlp())); extrasBatch.Put(toSlice(_bi.hash(), ExtraDetails), (ldb::Slice)dev::ref(BlockDetails((unsigned)pd.number + 1, td, _bi.parentHash, {}).rlp()));
extrasBatch.Put(toSlice(bi.hash(), ExtraLogBlooms), (ldb::Slice)dev::ref(blb.rlp())); extrasBatch.Put(toSlice(_bi.hash(), ExtraLogBlooms), (ldb::Slice)dev::ref(blb.rlp()));
extrasBatch.Put(toSlice(bi.hash(), ExtraReceipts), (ldb::Slice)dev::ref(br.rlp())); extrasBatch.Put(toSlice(_bi.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();
@ -526,20 +528,20 @@ ImportRoute BlockChain::import(bytes const& _block, OverlayDB const& _db, Import
{ {
clog(BlockChainWarn) << " Malformed block: " << diagnostic_information(_e); clog(BlockChainWarn) << " Malformed block: " << diagnostic_information(_e);
_e << errinfo_comment("Malformed block "); _e << errinfo_comment("Malformed block ");
clog(BlockChainWarn) << "Block: " << bi.hash(); clog(BlockChainWarn) << "Block: " << _bi.hash();
clog(BlockChainWarn) << bi; clog(BlockChainWarn) << _bi;
clog(BlockChainWarn) << "Block parent: " << bi.parentHash; clog(BlockChainWarn) << "Block parent: " << _bi.parentHash;
clog(BlockChainWarn) << BlockInfo(block(bi.parentHash)); clog(BlockChainWarn) << BlockInfo(block(_bi.parentHash));
throw; throw;
} }
#endif #endif
StructuredLogger::chainReceivedNewBlock( StructuredLogger::chainReceivedNewBlock(
bi.headerHash(WithoutNonce).abridged(), _bi.headerHash(WithoutNonce).abridged(),
bi.nonce.abridged(), _bi.nonce.abridged(),
currentHash().abridged(), currentHash().abridged(),
"", // TODO: remote id ?? "", // TODO: remote id ??
bi.parentHash.abridged() _bi.parentHash.abridged()
); );
// cnote << "Parent " << bi.parentHash << " has " << details(bi.parentHash).children.size() << " children."; // cnote << "Parent " << bi.parentHash << " has " << details(bi.parentHash).children.size() << " children.";
@ -552,8 +554,8 @@ ImportRoute BlockChain::import(bytes const& _block, OverlayDB const& _db, Import
// 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, _bi.parentHash);
route.push_back(bi.hash()); route.push_back(_bi.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)
@ -565,8 +567,8 @@ ImportRoute BlockChain::import(bytes const& _block, OverlayDB const& _db, Import
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 == _bi.hash())
tbi = bi; tbi = _bi;
else else
tbi = BlockInfo(block(*i)); tbi = BlockInfo(block(*i));
@ -593,7 +595,7 @@ ImportRoute BlockChain::import(bytes const& _block, OverlayDB const& _db, Import
h256s newTransactionAddresses; h256s newTransactionAddresses;
{ {
bytes blockBytes; bytes blockBytes;
RLP blockRLP(*i == bi.hash() ? _block : (blockBytes = block(*i))); RLP blockRLP(*i == _bi.hash() ? _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)
@ -609,17 +611,17 @@ ImportRoute BlockChain::import(bytes const& _block, OverlayDB const& _db, Import
// FINALLY! change our best hash. // FINALLY! change our best hash.
{ {
newLastBlockHash = bi.hash(); newLastBlockHash = _bi.hash();
newLastBlockNumber = (unsigned)bi.number; newLastBlockNumber = (unsigned)_bi.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 << " (#" << _bi.number << "). Has" << (details(_bi.parentHash).children.size() - 1) << "siblings. Route:" << route;
StructuredLogger::chainNewHead( StructuredLogger::chainNewHead(
bi.headerHash(WithoutNonce).abridged(), _bi.headerHash(WithoutNonce).abridged(),
bi.nonce.abridged(), _bi.nonce.abridged(),
currentHash().abridged(), currentHash().abridged(),
bi.parentHash.abridged() _bi.parentHash.abridged()
); );
} }
else else
@ -630,21 +632,21 @@ ImportRoute BlockChain::import(bytes const& _block, OverlayDB const& _db, Import
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 (isKnown(_bi.hash()) && !details(_bi.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:" << _bi;
clog(BlockChainDebug) << "DATABASE CORRUPTION: CRITICAL FAILURE"; clog(BlockChainDebug) << "DATABASE CORRUPTION: CRITICAL FAILURE";
exit(-1); exit(-1);
} }
try { try {
State canary(_db, *this, bi.hash()); State canary(_db, *this, _bi.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:" << _bi;
clog(BlockChainDebug) << "DATABASE CORRUPTION: CRITICAL FAILURE"; clog(BlockChainDebug) << "DATABASE CORRUPTION: CRITICAL FAILURE";
exit(-1); exit(-1);
} }
@ -981,14 +983,15 @@ vector<unsigned> BlockChain::withBlockBloom(LogBloom const& _b, unsigned _earlie
return ret; return ret;
} }
h256Hash BlockChain::allUnclesFrom(h256 const& _parent) const h256Hash BlockChain::allKinFrom(h256 const& _parent, unsigned _generations) const
{ {
// Get all uncles cited given a parent (i.e. featured as uncles/main in parent, parent + 1, ... parent + 5). // Get all uncles cited given a parent (i.e. featured as uncles/main in parent, parent + 1, ... parent + 5).
h256Hash ret;
h256 p = _parent; h256 p = _parent;
for (unsigned i = 0; i < 6 && p != m_genesisHash; ++i, p = details(p).parent) h256Hash ret = { p };
// p and (details(p).parent: i == 5) is likely to be overkill, but can't hurt to be cautious.
for (unsigned i = 0; i < _generations && p != m_genesisHash; ++i, p = details(p).parent)
{ {
ret.insert(p); // TODO: check: should this be details(p).parent? ret.insert(details(p).parent);
auto b = block(p); auto b = block(p);
for (auto i: RLP(b)[2]) for (auto i: RLP(b)[2])
ret.insert(sha3(i.data())); ret.insert(sha3(i.data()));

9
libethereum/BlockChain.h

@ -120,6 +120,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);
/// 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;
@ -203,14 +204,14 @@ public:
/// Get the hash of the genesis block. Thread-safe. /// Get the hash of the genesis block. Thread-safe.
h256 genesisHash() const { return m_genesisHash; } h256 genesisHash() const { return m_genesisHash; }
/// Get all blocks not allowed as uncles given a parent (i.e. featured as uncles/main in parent, parent + 1, ... parent + 5). /// Get all blocks not allowed as uncles given a parent (i.e. featured as uncles/main in parent, parent + 1, ... parent + @a _generations).
/// @returns set including the header-hash of every parent (including @a _parent) up to and including generation +5 /// @returns set including the header-hash of every parent (including @a _parent) up to and including generation + @a _generations
/// togther with all their quoted uncles. /// togther with all their quoted uncles.
h256Hash allUnclesFrom(h256 const& _parent) const; h256Hash allKinFrom(h256 const& _parent, unsigned _generations) const;
/// Run through database and verify all blocks by reevaluating. /// Run through database and verify all blocks by reevaluating.
/// Will call _progress with the progress in this operation first param done, second total. /// Will call _progress with the progress in this operation first param done, second total.
void rebuild(std::string const& _path, ProgressCallback const& _progress = std::function<void(unsigned, unsigned)>()); void rebuild(std::string const& _path, ProgressCallback const& _progress = std::function<void(unsigned, unsigned)>(), bool _prepPoW = false);
/** @returns a tuple of: /** @returns a tuple of:
* - an vector of hashes of all blocks between @a _from and @a _to, all blocks are ordered first by a number of * - an vector of hashes of all blocks between @a _from and @a _to, all blocks are ordered first by a number of

158
libethereum/BlockQueue.cpp

@ -20,7 +20,7 @@
*/ */
#include "BlockQueue.h" #include "BlockQueue.h"
#include <thread>
#include <libdevcore/Log.h> #include <libdevcore/Log.h>
#include <libethcore/Exceptions.h> #include <libethcore/Exceptions.h>
#include <libethcore/BlockInfo.h> #include <libethcore/BlockInfo.h>
@ -35,6 +35,108 @@ const char* BlockQueueChannel::name() { return EthOrange "[]>"; }
const char* BlockQueueChannel::name() { return EthOrange "▣┅▶"; } const char* BlockQueueChannel::name() { return EthOrange "▣┅▶"; }
#endif #endif
BlockQueue::BlockQueue()
{
// Allow some room for other activity
unsigned verifierThreads = std::max(thread::hardware_concurrency(), 3U) - 2U;
for (unsigned i = 0; i < verifierThreads; ++i)
m_verifiers.emplace_back([=](){
setThreadName("verifier" + toString(i));
this->verifierBody();
});
}
BlockQueue::~BlockQueue()
{
m_deleting = true;
m_moreToVerify.notify_all();
for (auto& i: m_verifiers)
i.join();
}
void BlockQueue::verifierBody()
{
while (!m_deleting)
{
std::pair<h256, bytes> work;
{
unique_lock<Mutex> l(m_verification);
m_moreToVerify.wait(l, [&](){ return !m_unverified.empty() || m_deleting; });
if (m_deleting)
return;
swap(work, m_unverified.front());
m_unverified.pop_front();
BlockInfo bi;
bi.mixHash = work.first;
m_verifying.push_back(make_pair(bi, bytes()));
}
std::pair<BlockInfo, bytes> res;
swap(work.second, res.second);
try {
res.first.populate(res.second, CheckEverything, work.first);
res.first.verifyInternals(&res.second);
RLP r(&res.second);
for (auto const& uncle: r[2])
BlockInfo().populateFromHeader(RLP(uncle.data()), CheckEverything);
}
catch (...)
{
// bad block.
{
// has to be this order as that's how invariants() assumes.
WriteGuard l2(m_lock);
unique_lock<Mutex> l(m_verification);
m_readySet.erase(work.first);
m_knownBad.insert(work.first);
}
unique_lock<Mutex> l(m_verification);
for (auto it = m_verifying.begin(); it != m_verifying.end(); ++it)
if (it->first.mixHash == work.first)
{
m_verifying.erase(it);
goto OK1;
}
cwarn << "GAA BlockQueue corrupt: job cancelled but cannot be found in m_verifying queue.";
OK1:;
continue;
}
bool ready = false;
{
unique_lock<Mutex> l(m_verification);
if (m_verifying.front().first.mixHash == work.first)
{
// we're next!
m_verifying.pop_front();
m_verified.push_back(move(res));
while (m_verifying.size() && !m_verifying.front().second.empty())
{
m_verified.push_back(move(m_verifying.front()));
m_verifying.pop_front();
}
ready = true;
}
else
{
for (auto& i: m_verifying)
if (i.first.mixHash == work.first)
{
i = move(res);
goto OK;
}
cwarn << "GAA BlockQueue corrupt: job finished but cannot be found in m_verifying queue.";
OK:;
}
}
if (ready)
m_onReady();
}
}
ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc, bool _isOurs) ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc, bool _isOurs)
{ {
// Check if we already know this block. // Check if we already know this block.
@ -110,11 +212,13 @@ 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.";
m_ready.push_back(make_pair(h, _block.toBytes())); DEV_GUARDED(m_verification)
m_unverified.push_back(make_pair(h, _block.toBytes()));
m_moreToVerify.notify_one();
m_readySet.insert(h); m_readySet.insert(h);
noteReady_WITH_LOCK(h); noteReady_WITH_LOCK(h);
m_onReady();
return ImportResult::Success; return ImportResult::Success;
} }
} }
@ -127,18 +231,19 @@ bool BlockQueue::doneDrain(h256s const& _bad)
m_drainingSet.clear(); m_drainingSet.clear();
if (_bad.size()) if (_bad.size())
{ {
vector<pair<h256, bytes>> old; vector<pair<BlockInfo, bytes>> old;
swap(m_ready, old); DEV_GUARDED(m_verification)
swap(m_verified, old);
for (auto& b: old) for (auto& b: old)
{ {
BlockInfo bi(b.second); if (m_knownBad.count(b.first.parentHash))
if (m_knownBad.count(bi.parentHash))
{ {
m_knownBad.insert(b.first); m_knownBad.insert(b.first.hash());
m_readySet.erase(b.first); m_readySet.erase(b.first.hash());
} }
else else
m_ready.push_back(std::move(b)); DEV_GUARDED(m_verification)
m_verified.push_back(std::move(b));
} }
} }
m_knownBad += _bad; m_knownBad += _bad;
@ -197,62 +302,73 @@ QueueStatus BlockQueue::blockStatus(h256 const& _h) const
QueueStatus::Unknown; QueueStatus::Unknown;
} }
void BlockQueue::drain(std::vector<bytes>& o_out, unsigned _max) void BlockQueue::drain(std::vector<std::pair<BlockInfo, bytes>>& 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())
{ {
o_out.resize(min<unsigned>(_max, m_ready.size())); DEV_GUARDED(m_verification)
for (unsigned i = 0; i < o_out.size(); ++i) {
swap(o_out[i], m_ready[i].second); o_out.resize(min<unsigned>(_max, m_verified.size()));
m_ready.erase(m_ready.begin(), advanced(m_ready.begin(), o_out.size())); for (unsigned i = 0; i < o_out.size(); ++i)
swap(o_out[i], m_verified[i]);
m_verified.erase(m_verified.begin(), advanced(m_verified.begin(), o_out.size()));
}
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 = BlockInfo::headerHash(bs); auto h = bs.first.hash();
m_drainingSet.insert(h); m_drainingSet.insert(h);
m_readySet.erase(h); m_readySet.erase(h);
} }
// swap(o_out, m_ready);
// swap(m_drainingSet, m_readySet);
} }
} }
bool BlockQueue::invariants() const bool BlockQueue::invariants() const
{ {
return m_readySet.size() == m_ready.size(); Guard l(m_verification);
return m_readySet.size() == m_verified.size() + m_unverified.size() + m_verifying.size();
} }
void BlockQueue::noteReady_WITH_LOCK(h256 const& _good) void BlockQueue::noteReady_WITH_LOCK(h256 const& _good)
{ {
DEV_INVARIANT_CHECK; DEV_INVARIANT_CHECK;
list<h256> goodQueue(1, _good); list<h256> goodQueue(1, _good);
bool notify = false;
while (!goodQueue.empty()) while (!goodQueue.empty())
{ {
auto r = m_unknown.equal_range(goodQueue.front()); auto r = m_unknown.equal_range(goodQueue.front());
goodQueue.pop_front(); goodQueue.pop_front();
for (auto it = r.first; it != r.second; ++it) for (auto it = r.first; it != r.second; ++it)
{ {
m_ready.push_back(it->second); DEV_GUARDED(m_verification)
m_unverified.push_back(it->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);
goodQueue.push_back(newReady); goodQueue.push_back(newReady);
notify = true;
} }
m_unknown.erase(r.first, r.second); m_unknown.erase(r.first, r.second);
} }
if (notify)
m_moreToVerify.notify_all();
} }
void BlockQueue::retryAllUnknown() void BlockQueue::retryAllUnknown()
{ {
WriteGuard l(m_lock);
DEV_INVARIANT_CHECK; DEV_INVARIANT_CHECK;
for (auto it = m_unknown.begin(); it != m_unknown.end(); ++it) for (auto it = m_unknown.begin(); it != m_unknown.end(); ++it)
{ {
m_ready.push_back(it->second); DEV_GUARDED(m_verification)
m_unverified.push_back(it->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_moreToVerify.notify_one();
} }
m_unknown.clear(); m_unknown.clear();
m_moreToVerify.notify_all();
} }

35
libethereum/BlockQueue.h

@ -21,12 +21,16 @@
#pragma once #pragma once
#include <condition_variable>
#include <thread>
#include <deque>
#include <boost/thread.hpp> #include <boost/thread.hpp>
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include <libdevcore/Log.h> #include <libdevcore/Log.h>
#include <libethcore/Common.h> #include <libethcore/Common.h>
#include <libdevcore/Guards.h> #include <libdevcore/Guards.h>
#include <libethcore/Common.h> #include <libethcore/Common.h>
#include <libethcore/BlockInfo.h>
namespace dev namespace dev
{ {
@ -41,7 +45,9 @@ struct BlockQueueChannel: public LogChannel { static const char* name(); static
struct BlockQueueStatus struct BlockQueueStatus
{ {
size_t ready; size_t verified;
size_t verifying;
size_t unverified;
size_t future; size_t future;
size_t unknown; size_t unknown;
size_t bad; size_t bad;
@ -64,6 +70,9 @@ enum class QueueStatus
class BlockQueue: HasInvariants class BlockQueue: HasInvariants
{ {
public: 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 _tx, BlockChain const& _bc, bool _isOurs = false);
@ -72,7 +81,7 @@ public:
/// 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<bytes>& o_out, unsigned _max); void drain(std::vector<std::pair<BlockInfo, bytes>>& 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.
@ -85,16 +94,16 @@ public:
void retryAllUnknown(); void retryAllUnknown();
/// Get information on the items queued. /// Get information on the items queued.
std::pair<unsigned, unsigned> items() const { ReadGuard l(m_lock); return std::make_pair(m_ready.size(), m_unknown.size()); } std::pair<unsigned, unsigned> items() const { ReadGuard l(m_lock); return std::make_pair(m_verified.size(), m_unknown.size()); }
/// Clear everything. /// Clear everything.
void clear() { WriteGuard l(m_lock); DEV_INVARIANT_CHECK; m_readySet.clear(); m_drainingSet.clear(); m_ready.clear(); m_unknownSet.clear(); m_unknown.clear(); m_future.clear(); } 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(); }
/// 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); return BlockQueueStatus{m_ready.size(), m_future.size(), m_unknown.size(), m_knownBad.size()}; } 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()}; }
/// 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;
@ -106,15 +115,25 @@ private:
bool invariants() const override; bool invariants() const override;
mutable boost::shared_mutex m_lock; ///< General lock. void verifierBody();
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.
h256Hash m_readySet; ///< All blocks ready for chain-import. h256Hash m_readySet; ///< All blocks ready for chain import.
std::vector<std::pair<h256, bytes>> m_ready; ///< List of blocks, in correct order, ready for chain-import.
h256Hash m_unknownSet; ///< Set of all blocks whose parents are not ready/in-chain. h256Hash m_unknownSet; ///< Set of all blocks whose parents are not ready/in-chain.
std::unordered_multimap<h256, std::pair<h256, bytes>> m_unknown; ///< For blocks that have an unknown parent; we map their parent hash to the block stuff, and insert once the block appears. std::unordered_multimap<h256, std::pair<h256, bytes>> m_unknown; ///< For blocks that have an unknown parent; we map their parent hash to the block stuff, and insert once the block appears.
h256Hash m_knownBad; ///< Set of blocks that we know will never be valid. 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.
mutable Mutex m_verification; ///< Mutex that allows writing to m_verified, m_verifying and m_unverified.
std::condition_variable m_moreToVerify; ///< Signaled when m_unverified has a new entry.
std::vector<std::pair<BlockInfo, bytes>> m_verified; ///< List of blocks, in correct order, verified and ready for chain-import.
std::deque<std::pair<BlockInfo, bytes>> m_verifying; ///< List of blocks being verified; as long as the second component (bytes) is empty, it's not finished.
std::deque<std::pair<h256, bytes>> m_unverified; ///< List of blocks, in correct order, ready for verification.
std::vector<std::thread> m_verifiers; ///< Threads who only verify.
bool m_deleting = false; ///< Exit condition for verifiers.
}; };
} }

21
libethereum/Client.cpp

@ -461,13 +461,13 @@ ProofOfWork::WorkPackage Client::getWork()
bool Client::submitWork(ProofOfWork::Solution const& _solution) bool Client::submitWork(ProofOfWork::Solution const& _solution)
{ {
bytes newBlock; bytes newBlock;
DEV_TIMED(working) DEV_WRITE_GUARDED(x_working) DEV_WRITE_GUARDED(x_working)
if (!m_working.completeMine<ProofOfWork>(_solution)) if (!m_working.completeMine<ProofOfWork>(_solution))
return false; return false;
DEV_READ_GUARDED(x_working) DEV_READ_GUARDED(x_working)
{ {
DEV_TIMED(post) DEV_WRITE_GUARDED(x_postMine) DEV_WRITE_GUARDED(x_postMine)
m_postMine = m_working; m_postMine = m_working;
newBlock = m_working.blockData(); newBlock = m_working.blockData();
} }
@ -484,7 +484,7 @@ void Client::syncBlockQueue()
cwork << "BQ ==> CHAIN ==> STATE"; cwork << "BQ ==> CHAIN ==> STATE";
{ {
tie(ir.first, ir.second, m_syncBlockQueue) = m_bc.sync(m_bq, m_stateDB, 100); tie(ir.first, ir.second, m_syncBlockQueue) = m_bc.sync(m_bq, m_stateDB, 10);
if (ir.first.empty()) if (ir.first.empty())
return; return;
} }
@ -499,14 +499,14 @@ void Client::syncTransactionQueue()
h256Hash changeds; h256Hash changeds;
TransactionReceipts newPendingReceipts; TransactionReceipts newPendingReceipts;
DEV_TIMED(working) DEV_WRITE_GUARDED(x_working) DEV_WRITE_GUARDED(x_working)
tie(newPendingReceipts, m_syncTransactionQueue) = m_working.sync(m_bc, m_tq, *m_gp); tie(newPendingReceipts, m_syncTransactionQueue) = m_working.sync(m_bc, m_tq, *m_gp);
if (newPendingReceipts.empty()) if (newPendingReceipts.empty())
return; return;
DEV_READ_GUARDED(x_working) DEV_READ_GUARDED(x_working)
DEV_TIMED(post) DEV_WRITE_GUARDED(x_postMine) DEV_WRITE_GUARDED(x_postMine)
m_postMine = m_working; m_postMine = m_working;
DEV_READ_GUARDED(x_postMine) DEV_READ_GUARDED(x_postMine)
@ -574,7 +574,7 @@ void Client::onChainChanged(ImportRoute const& _ir)
DEV_WRITE_GUARDED(x_preMine) DEV_WRITE_GUARDED(x_preMine)
m_preMine = newPreMine; m_preMine = newPreMine;
DEV_TIMED(working) DEV_WRITE_GUARDED(x_working) DEV_WRITE_GUARDED(x_working)
m_working = newPreMine; m_working = newPreMine;
DEV_READ_GUARDED(x_postMine) DEV_READ_GUARDED(x_postMine)
for (auto const& t: m_postMine.pending()) for (auto const& t: m_postMine.pending())
@ -584,7 +584,7 @@ void Client::onChainChanged(ImportRoute const& _ir)
if (ir != ImportResult::Success) if (ir != ImportResult::Success)
onTransactionQueueReady(); onTransactionQueueReady();
} }
DEV_READ_GUARDED(x_working) DEV_TIMED(post) DEV_WRITE_GUARDED(x_postMine) DEV_READ_GUARDED(x_working) DEV_WRITE_GUARDED(x_postMine)
m_postMine = m_working; m_postMine = m_working;
changeds.insert(PendingChangedFilter); changeds.insert(PendingChangedFilter);
@ -606,14 +606,15 @@ bool Client::remoteActive() const
void Client::onPostStateChanged() void Client::onPostStateChanged()
{ {
cnote << "Post state changed: Restarting mining..."; cnote << "Post state changed";
if (isMining() || remoteActive()) if (isMining() || remoteActive())
{ {
DEV_TIMED(working) DEV_WRITE_GUARDED(x_working) cnote << "Restarting mining...";
DEV_WRITE_GUARDED(x_working)
m_working.commitToMine(m_bc); m_working.commitToMine(m_bc);
DEV_READ_GUARDED(x_working) DEV_READ_GUARDED(x_working)
{ {
DEV_TIMED(post) DEV_WRITE_GUARDED(x_postMine) DEV_WRITE_GUARDED(x_postMine)
m_postMine = m_working; m_postMine = m_working;
m_miningInfo = m_postMine.info(); m_miningInfo = m_postMine.info();
} }

2
libethereum/Client.h

@ -37,12 +37,12 @@
#include <libdevcore/Worker.h> #include <libdevcore/Worker.h>
#include <libethcore/Params.h> #include <libethcore/Params.h>
#include <libethcore/ABI.h> #include <libethcore/ABI.h>
#include <libethcore/Farm.h>
#include <libp2p/Common.h> #include <libp2p/Common.h>
#include "CanonBlockChain.h" #include "CanonBlockChain.h"
#include "TransactionQueue.h" #include "TransactionQueue.h"
#include "State.h" #include "State.h"
#include "CommonNet.h" #include "CommonNet.h"
#include "Farm.h"
#include "ClientBase.h" #include "ClientBase.h"
namespace dev namespace dev

2
libethereum/ClientBase.cpp

@ -57,7 +57,7 @@ void ClientBase::submitTransaction(Secret _secret, u256 _value, Address _dest, b
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; cnote << "New transaction " << t << "(maxNonce for sender" << a << "is" << 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)

5
libethereum/EthereumHost.cpp

@ -83,7 +83,7 @@ void EthereumHost::noteNeedsSyncing(EthereumPeer* _who)
_who->attemptSync(); _who->attemptSync();
} }
void EthereumHost::changeSyncer(EthereumPeer* _syncer) void EthereumHost::changeSyncer(EthereumPeer* _syncer, bool _needHelp)
{ {
if (_syncer) if (_syncer)
clog(NetAllDetail) << "Changing syncer to" << _syncer->session()->socketId(); clog(NetAllDetail) << "Changing syncer to" << _syncer->session()->socketId();
@ -93,9 +93,10 @@ void EthereumHost::changeSyncer(EthereumPeer* _syncer)
m_syncer = _syncer; m_syncer = _syncer;
if (isSyncing()) if (isSyncing())
{ {
if (_syncer->m_asking == Asking::Blocks) if (_needHelp && _syncer->m_asking == Asking::Blocks)
for (auto j: peerSessions()) for (auto j: peerSessions())
{ {
clog(NetNote) << "Getting help with downloading blocks";
auto e = j.first->cap<EthereumPeer>().get(); auto e = j.first->cap<EthereumPeer>().get();
if (e != _syncer && e->m_asking == Asking::Nothing) if (e != _syncer && e->m_asking == Asking::Nothing)
e->transition(Asking::Blocks); e->transition(Asking::Blocks);

2
libethereum/EthereumHost.h

@ -108,7 +108,7 @@ private:
virtual void onStarting() { startWorking(); } virtual void onStarting() { startWorking(); }
virtual void onStopping() { stopWorking(); } virtual void onStopping() { stopWorking(); }
void changeSyncer(EthereumPeer* _ignore); void changeSyncer(EthereumPeer* _ignore, bool _needHelp = true);
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.

13
libethereum/EthereumPeer.cpp

@ -74,7 +74,7 @@ string toString(Asking _a)
return "?"; return "?";
} }
void EthereumPeer::transition(Asking _a, bool _force) void EthereumPeer::transition(Asking _a, bool _force, bool _needHelp)
{ {
clog(NetMessageSummary) << "Transition!" << ::toString(_a) << "from" << ::toString(m_asking) << ", " << (isSyncing() ? "syncing" : "holding") << (needsSyncing() ? "& needed" : ""); clog(NetMessageSummary) << "Transition!" << ::toString(_a) << "from" << ::toString(m_asking) << ", " << (isSyncing() ? "syncing" : "holding") << (needsSyncing() ? "& needed" : "");
@ -151,7 +151,7 @@ void EthereumPeer::transition(Asking _a, bool _force)
if (m_asking == Asking::Nothing || m_asking == Asking::Hashes || m_asking == Asking::Blocks) if (m_asking == Asking::Nothing || m_asking == Asking::Hashes || m_asking == Asking::Blocks)
{ {
// Looks like it's the best yet for total difficulty. Set to download. // Looks like it's the best yet for total difficulty. Set to download.
setAsking(Asking::Blocks, isSyncing()); // will kick off other peers to help if available. setAsking(Asking::Blocks, isSyncing(), _needHelp); // will kick off other peers to help if available.
auto blocks = m_sub.nextFetch(c_maxBlocksAsk); auto blocks = m_sub.nextFetch(c_maxBlocksAsk);
if (blocks.size()) if (blocks.size())
{ {
@ -200,13 +200,13 @@ void EthereumPeer::transition(Asking _a, bool _force)
clog(NetWarn) << "Invalid state transition:" << ::toString(_a) << "from" << ::toString(m_asking) << ", " << (isSyncing() ? "syncing" : "holding") << (needsSyncing() ? "& needed" : ""); clog(NetWarn) << "Invalid state transition:" << ::toString(_a) << "from" << ::toString(m_asking) << ", " << (isSyncing() ? "syncing" : "holding") << (needsSyncing() ? "& needed" : "");
} }
void EthereumPeer::setAsking(Asking _a, bool _isSyncing) void EthereumPeer::setAsking(Asking _a, bool _isSyncing, bool _needHelp)
{ {
bool changedAsking = (m_asking != _a); bool changedAsking = (m_asking != _a);
m_asking = _a; m_asking = _a;
if (_isSyncing != (host()->m_syncer == this) || (_isSyncing && changedAsking)) if (_isSyncing != (host()->m_syncer == this) || (_isSyncing && changedAsking))
host()->changeSyncer(_isSyncing ? this : nullptr); host()->changeSyncer(_isSyncing ? this : nullptr, _needHelp);
if (!_isSyncing) if (!_isSyncing)
{ {
@ -599,9 +599,10 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
clog(NetMessageSummary) << knowns << "knowns," << unknowns << "unknowns"; clog(NetMessageSummary) << knowns << "knowns," << unknowns << "unknowns";
if (unknowns > 0) if (unknowns > 0)
{ {
clog(NetNote) << "Not syncing and new block hash discovered: syncing without help.";
host()->m_man.resetToChain(m_syncingNeededBlocks); host()->m_man.resetToChain(m_syncingNeededBlocks);
host()->changeSyncer(this); host()->changeSyncer(this, false);
transition(Asking::Blocks); transition(Asking::Blocks, false, false); // TODO: transaction(Asking::NewBlocks, false)
} }
return true; return true;
} }

4
libethereum/EthereumPeer.h

@ -77,7 +77,7 @@ private:
virtual bool interpret(unsigned _id, RLP const& _r); virtual bool interpret(unsigned _id, RLP const& _r);
/// Transition state in a particular direction. /// Transition state in a particular direction.
void transition(Asking _wantState, bool _force = false); void transition(Asking _wantState, bool _force = false, bool _needHelp = true);
/// Attempt to begin syncing with this peer; first check the peer has a more difficlult chain to download, then start asking for hashes, then move to blocks. /// Attempt to begin syncing with this peer; first check the peer has a more difficlult chain to download, then start asking for hashes, then move to blocks.
void attemptSync(); void attemptSync();
@ -89,7 +89,7 @@ private:
void clearKnownTransactions() { std::lock_guard<std::mutex> l(x_knownTransactions); m_knownTransactions.clear(); } void clearKnownTransactions() { std::lock_guard<std::mutex> l(x_knownTransactions); m_knownTransactions.clear(); }
/// Update our asking state. /// Update our asking state.
void setAsking(Asking _g, bool _isSyncing); void setAsking(Asking _g, bool _isSyncing, bool _needHelp = true);
/// Update our syncing requirements state. /// Update our syncing requirements state.
void setNeedsSyncing(h256 _latestHash, u256 _td); void setNeedsSyncing(h256 _latestHash, u256 _td);

3
libethereum/Executive.cpp

@ -246,6 +246,9 @@ bool Executive::go(OnOpFunc const& _onOp)
m_endGas = 0; m_endGas = 0;
m_excepted = toTransactionException(_e); m_excepted = toTransactionException(_e);
m_ext->revert(); m_ext->revert();
if (m_isCreation)
m_newAddress = Address();
} }
catch (Exception const& _e) catch (Exception const& _e)
{ {

2
libethereum/Executive.h

@ -137,7 +137,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; bigint m_gasRequired; ///< Gas required during execution of the transaction.
bigint m_gasCost; bigint m_gasCost;
bigint m_totalCost; bigint m_totalCost;
}; };

0
libethereum/Farm.cpp

67
libethereum/State.cpp

@ -29,6 +29,7 @@
#include <libdevcore/CommonIO.h> #include <libdevcore/CommonIO.h>
#include <libdevcore/Assertions.h> #include <libdevcore/Assertions.h>
#include <libdevcore/StructuredLogger.h> #include <libdevcore/StructuredLogger.h>
#include <libdevcrypto/TrieHash.h>
#include <libevmcore/Instruction.h> #include <libevmcore/Instruction.h>
#include <libethcore/Exceptions.h> #include <libethcore/Exceptions.h>
#include <libevm/VMFactory.h> #include <libevm/VMFactory.h>
@ -62,6 +63,7 @@ OverlayDB State::openDB(std::string _path, WithExisting _we)
boost::filesystem::remove_all(_path + "/state"); boost::filesystem::remove_all(_path + "/state");
ldb::Options o; ldb::Options o;
o.max_open_files = 256;
o.create_if_missing = true; o.create_if_missing = true;
ldb::DB* db = nullptr; ldb::DB* db = nullptr;
ldb::DB::Open(o, _path + "/state", &db); ldb::DB::Open(o, _path + "/state", &db);
@ -113,7 +115,7 @@ State::State(OverlayDB const& _db, BaseState _bs, Address _coinbaseAddress):
paranoia("end of normal construction.", true); paranoia("end of normal construction.", true);
} }
State::State(OverlayDB const& _db, BlockChain const& _bc, h256 _h): State::State(OverlayDB const& _db, BlockChain const& _bc, h256 _h, ImportRequirements::value _ir):
m_db(_db), m_db(_db),
m_state(&m_db), m_state(&m_db),
m_blockReward(c_blockReward) m_blockReward(c_blockReward)
@ -135,18 +137,18 @@ State::State(OverlayDB const& _db, BlockChain const& _bc, h256 _h):
// 1. Start at parent's end state (state root). // 1. Start at parent's end state (state root).
BlockInfo bip; BlockInfo bip;
bip.populate(_bc.block(bi.parentHash)); bip.populate(_bc.block(bi.parentHash));
sync(_bc, bi.parentHash, bip); sync(_bc, bi.parentHash, bip, _ir);
// 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); enact(&b, _bc, _ir);
} }
else else
{ {
// Genesis required: // Genesis required:
// We know there are no transactions, so just populate directly. // We know there are no transactions, so just populate directly.
m_state.init(); m_state.init();
sync(_bc, _h, bi); sync(_bc, _h, bi, _ir);
} }
} }
@ -515,10 +517,10 @@ pair<TransactionReceipts, bool> State::sync(BlockChain const& _bc, TransactionQu
cnote << i.first << "Dropping old transaction (nonce too low)"; cnote << i.first << "Dropping old transaction (nonce too low)";
_tq.drop(i.first); _tq.drop(i.first);
} }
else if (got > req + 25) else if (got > req + _tq.waiting(i.second.sender()))
{ {
// too new // too new
cnote << i.first << "Dropping new transaction (> 25 nonces ahead)"; cnote << i.first << "Dropping new transaction (too many nonces ahead)";
_tq.drop(i.first); _tq.drop(i.first);
} }
else else
@ -533,7 +535,12 @@ pair<TransactionReceipts, bool> State::sync(BlockChain const& _bc, TransactionQu
_tq.drop(i.first); _tq.drop(i.first);
} }
else else
_tq.setFuture(i); {
// Temporarily no gas left in current block.
// OPTIMISE: could note this and then we don't evaluate until a block that does have the gas left.
// for now, just leave alone.
// _tq.setFuture(i);
}
} }
catch (Exception const& _e) catch (Exception const& _e)
{ {
@ -591,34 +598,33 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, ImportRequirement
LastHashes lh = _bc.lastHashes((unsigned)m_previousBlock.number); LastHashes lh = _bc.lastHashes((unsigned)m_previousBlock.number);
RLP rlp(_block); RLP rlp(_block);
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]) for (auto const& tr: rlp[1])
{ {
RLPStream k;
k << i;
transactionsTrie.insert(&k.out(), tr.data());
execute(lh, Transaction(tr.data(), CheckTransaction::Everything)); execute(lh, Transaction(tr.data(), CheckTransaction::Everything));
RLPStream receiptRLP;
RLPStream receiptrlp; m_receipts.back().streamRLP(receiptRLP);
m_receipts.back().streamRLP(receiptrlp); receipts.push_back(receiptRLP.out());
receiptsTrie.insert(&k.out(), &receiptrlp.out());
++i; ++i;
} }
if (receiptsTrie.root() != m_currentBlock.receiptsRoot) auto receiptsRoot = orderedTrieRoot(receipts);
if (receiptsRoot != m_currentBlock.receiptsRoot)
{ {
cwarn << "Bad receipts state root."; cwarn << "Bad receipts state root.";
cwarn << "Expected: " << toString(receiptsTrie.root()) << " received: " << toString(m_currentBlock.receiptsRoot); cwarn << "Expected: " << toString(receiptsRoot) << " received: " << toString(m_currentBlock.receiptsRoot);
cwarn << "Block:" << toHex(_block); cwarn << "Block:" << toHex(_block);
cwarn << "Block RLP:" << rlp; cwarn << "Block RLP:" << rlp;
cwarn << "Calculated: " << receiptsTrie.root(); cwarn << "Calculated: " << receiptsRoot;
for (unsigned j = 0; j < i; ++j) for (unsigned j = 0; j < i; ++j)
{ {
RLPStream k; RLPStream k;
k << j; k << j;
auto b = asBytes(receiptsTrie.at(&k.out())); auto b = receipts[j];
cwarn << j << ": "; cwarn << j << ": ";
cwarn << "RLP: " << RLP(b); cwarn << "RLP: " << RLP(b);
cwarn << "Hex: " << toHex(b); cwarn << "Hex: " << toHex(b);
@ -650,25 +656,23 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, ImportRequirement
if (rlp[2].itemCount() > 2) if (rlp[2].itemCount() > 2)
BOOST_THROW_EXCEPTION(TooManyUncles()); BOOST_THROW_EXCEPTION(TooManyUncles());
unordered_set<Nonce> nonces = { m_currentBlock.nonce };
vector<BlockInfo> rewarded; vector<BlockInfo> rewarded;
h256Hash knownUncles = _bc.allUnclesFrom(m_currentBlock.parentHash); h256Hash excluded = _bc.allKinFrom(m_currentBlock.parentHash, 6);
excluded.insert(m_currentBlock.hash());
for (auto const& i: rlp[2]) for (auto const& i: rlp[2])
{ {
if (knownUncles.count(sha3(i.data()))) auto h = sha3(i.data());
BOOST_THROW_EXCEPTION(UncleInChain() << errinfo_comment("Uncle in block already mentioned") << errinfo_data(toString(knownUncles)) << errinfo_hash256(sha3(i.data())) ); if (excluded.count(h))
BOOST_THROW_EXCEPTION(UncleInChain() << errinfo_comment("Uncle in block already mentioned") << errinfo_data(toString(excluded)) << errinfo_hash256(sha3(i.data())));
BlockInfo uncle = BlockInfo::fromHeader(i.data()); excluded.insert(h);
if (nonces.count(uncle.nonce))
BOOST_THROW_EXCEPTION(DuplicateUncleNonce());
BlockInfo uncle = BlockInfo::fromHeader(i.data(), (_ir & ImportRequirements::CheckUncles) ? CheckEverything : IgnoreNonce, h);
BlockInfo uncleParent(_bc.block(uncle.parentHash)); BlockInfo uncleParent(_bc.block(uncle.parentHash));
if ((bigint)uncleParent.number < (bigint)m_currentBlock.number - 7) if ((bigint)uncleParent.number < (bigint)m_currentBlock.number - 7)
BOOST_THROW_EXCEPTION(UncleTooOld()); BOOST_THROW_EXCEPTION(UncleTooOld());
uncle.verifyParent(uncleParent); uncle.verifyParent(uncleParent);
nonces.insert(uncle.nonce);
// tdIncrease += uncle.difficulty; // tdIncrease += uncle.difficulty;
rewarded.push_back(uncle); rewarded.push_back(uncle);
} }
@ -817,14 +821,14 @@ void State::commitToMine(BlockChain const& _bc)
{ {
// Find great-uncles (or second-cousins or whatever they are) - children of great-grandparents, great-great-grandparents... that were not already uncles in previous generations. // Find great-uncles (or second-cousins or whatever they are) - children of great-grandparents, great-great-grandparents... that were not already uncles in previous generations.
// cout << "Checking " << m_previousBlock.hash << ", parent=" << m_previousBlock.parentHash << endl; // cout << "Checking " << m_previousBlock.hash << ", parent=" << m_previousBlock.parentHash << endl;
h256Hash knownUncles = _bc.allUnclesFrom(m_currentBlock.parentHash); h256Hash excluded = _bc.allKinFrom(m_currentBlock.parentHash, 6);
auto p = m_previousBlock.parentHash; auto p = m_previousBlock.parentHash;
for (unsigned gen = 0; gen < 6 && p != _bc.genesisHash() && unclesCount < 2; ++gen, p = _bc.details(p).parent) for (unsigned gen = 0; gen < 6 && p != _bc.genesisHash() && unclesCount < 2; ++gen, p = _bc.details(p).parent)
{ {
auto us = _bc.details(p).children; auto us = _bc.details(p).children;
assert(us.size() >= 1); // must be at least 1 child of our grandparent - it's our own parent! assert(us.size() >= 1); // must be at least 1 child of our grandparent - it's our own parent!
for (auto const& u: us) for (auto const& u: us)
if (!knownUncles.count(u)) // ignore any uncles/mainline blocks that we know about. if (!excluded.count(u)) // ignore any uncles/mainline blocks that we know about.
{ {
BlockInfo ubi(_bc.block(u)); BlockInfo ubi(_bc.block(u));
ubi.streamRLP(unclesData, WithNonce); ubi.streamRLP(unclesData, WithNonce);
@ -836,6 +840,9 @@ void State::commitToMine(BlockChain const& _bc)
} }
} }
// TODO: move over to using TrieHash
MemoryDB tm; MemoryDB tm;
GenericTrieDB<MemoryDB> transactionsTrie(&tm); GenericTrieDB<MemoryDB> transactionsTrie(&tm);
transactionsTrie.init(); transactionsTrie.init();

2
libethereum/State.h

@ -118,7 +118,7 @@ public:
explicit State(OverlayDB const& _db, BaseState _bs = BaseState::PreExisting, Address _coinbaseAddress = Address()); explicit State(OverlayDB const& _db, BaseState _bs = BaseState::PreExisting, Address _coinbaseAddress = Address());
/// Construct state object from arbitrary point in blockchain. /// Construct state object from arbitrary point in blockchain.
State(OverlayDB const& _db, BlockChain const& _bc, h256 _hash); State(OverlayDB const& _db, BlockChain const& _bc, h256 _hash, ImportRequirements::value _ir = ImportRequirements::Default);
/// Copy state object. /// Copy state object.
State(State const& _s); State(State const& _s);

147
libethereum/TransactionQueue.cpp

@ -35,15 +35,26 @@ ImportResult TransactionQueue::import(bytesConstRef _transactionRLP, ImportCallb
// Check if we already know this transaction. // Check if we already know this transaction.
h256 h = sha3(_transactionRLP); h256 h = sha3(_transactionRLP);
Transaction t;
ImportResult ir;
{
UpgradableGuard l(m_lock); UpgradableGuard l(m_lock);
auto ir = check_WITH_LOCK(h, _ik); ir = check_WITH_LOCK(h, _ik);
if (ir != ImportResult::Success) if (ir != ImportResult::Success)
return ir; return ir;
Transaction t(_transactionRLP, CheckTransaction::Everything); try {
UpgradeGuard ul(l); t = Transaction(_transactionRLP, CheckTransaction::Everything);
return manageImport_WITH_LOCK(h, t, _cb); UpgradeGuard ul(l);
ir = manageImport_WITH_LOCK(h, t, _cb);
}
catch (...) {
return ImportResult::Malformed;
}
}
// cdebug << "import-END: Nonce of" << t.sender() << "now" << maxNonce(t.sender());
return ir;
} }
ImportResult TransactionQueue::check_WITH_LOCK(h256 const& _h, IfDropped _ik) ImportResult TransactionQueue::check_WITH_LOCK(h256 const& _h, IfDropped _ik)
@ -62,15 +73,33 @@ ImportResult TransactionQueue::import(Transaction const& _transaction, ImportCal
// Check if we already know this transaction. // Check if we already know this transaction.
h256 h = _transaction.sha3(WithSignature); h256 h = _transaction.sha3(WithSignature);
UpgradableGuard l(m_lock); // cdebug << "import-BEGIN: Nonce of sender" << maxNonce(_transaction.sender());
// TODO: keep old transactions around and check in State for nonce validity ImportResult ret;
{
UpgradableGuard l(m_lock);
// TODO: keep old transactions around and check in State for nonce validity
auto ir = check_WITH_LOCK(h, _ik); auto ir = check_WITH_LOCK(h, _ik);
if (ir != ImportResult::Success) if (ir != ImportResult::Success)
return ir; return ir;
UpgradeGuard ul(l); {
return manageImport_WITH_LOCK(h, _transaction, _cb); UpgradeGuard ul(l);
ret = manageImport_WITH_LOCK(h, _transaction, _cb);
}
}
// cdebug << "import-END: Nonce of" << _transaction.sender() << "now" << maxNonce(_transaction.sender());
return ret;
}
std::unordered_map<h256, Transaction> TransactionQueue::transactions() const
{
ReadGuard l(m_lock);
auto ret = m_current;
for (auto const& i: m_future)
if (i.second.nonce() < maxNonce_WITH_LOCK(i.second.sender()))
ret.insert(i);
return ret;
} }
ImportResult TransactionQueue::manageImport_WITH_LOCK(h256 const& _h, Transaction const& _transaction, ImportCallback const& _cb) ImportResult TransactionQueue::manageImport_WITH_LOCK(h256 const& _h, Transaction const& _transaction, ImportCallback const& _cb)
@ -105,62 +134,100 @@ ImportResult TransactionQueue::manageImport_WITH_LOCK(h256 const& _h, Transactio
u256 TransactionQueue::maxNonce(Address const& _a) const u256 TransactionQueue::maxNonce(Address const& _a) const
{ {
cdebug << "txQ::maxNonce" << _a; // cdebug << "txQ::maxNonce" << _a;
ReadGuard l(m_lock); ReadGuard l(m_lock);
return maxNonce_WITH_LOCK(_a);
}
u256 TransactionQueue::maxNonce_WITH_LOCK(Address const& _a) const
{
u256 ret = 0; u256 ret = 0;
auto r = m_senders.equal_range(_a); auto r = m_senders.equal_range(_a);
for (auto it = r.first; it != r.second; ++it) for (auto it = r.first; it != r.second; ++it)
{ if (m_current.count(it->second))
cdebug << it->first << "1+" << m_current.at(it->second).nonce(); {
DEV_IGNORE_EXCEPTIONS(ret = max(ret, m_current.at(it->second).nonce() + 1)); // cdebug << it->first << "1+" << m_current.at(it->second).nonce();
} ret = max(ret, m_current.at(it->second).nonce() + 1);
}
else if (m_future.count(it->second))
{
// cdebug << it->first << "1+" << m_future.at(it->second).nonce();
ret = max(ret, m_future.at(it->second).nonce() + 1);
}
else
{
cwarn << "ERRROR!!!!! m_senders references non-current transaction";
cwarn << "Sender" << it->first << "has transaction" << it->second;
cwarn << "Count of m_current for" << it->second << "is" << m_current.count(it->second);
}
return ret; return ret;
} }
void TransactionQueue::insertCurrent_WITH_LOCK(std::pair<h256, Transaction> const& _p) void TransactionQueue::insertCurrent_WITH_LOCK(std::pair<h256, Transaction> const& _p)
{ {
cdebug << "txQ::insertCurrent" << _p.first << _p.second.sender() << _p.second.nonce(); // cdebug << "txQ::insertCurrent" << _p.first << _p.second.sender() << _p.second.nonce();
m_senders.insert(make_pair(_p.second.sender(), _p.first)); m_senders.insert(make_pair(_p.second.sender(), _p.first));
if (m_current.count(_p.first))
cwarn << "Transaction hash" << _p.first << "already in current?!";
m_current.insert(_p); m_current.insert(_p);
} }
bool TransactionQueue::removeCurrent_WITH_LOCK(h256 const& _txHash) bool TransactionQueue::remove_WITH_LOCK(h256 const& _txHash)
{ {
cdebug << "txQ::removeCurrent" << _txHash; // cdebug << "txQ::remove" << _txHash;
if (m_current.count(_txHash)) for (std::unordered_map<h256, Transaction>* pool: { &m_current, &m_future })
{ {
auto r = m_senders.equal_range(m_current[_txHash].sender()); auto pit = pool->find(_txHash);
for (auto it = r.first; it != r.second; ++it) if (pit != pool->end())
if (it->second == _txHash) {
{ auto r = m_senders.equal_range(pit->second.sender());
cdebug << "=> sender" << it->first; for (auto i = r.first; i != r.second; ++i)
m_senders.erase(it); if (i->second == _txHash)
break; {
} m_senders.erase(i);
cdebug << "=> nonce" << m_current[_txHash].nonce(); break;
m_current.erase(_txHash); }
return true; // cdebug << "=> nonce" << pit->second.nonce();
pool->erase(pit);
return true;
}
} }
return false; return false;
} }
unsigned TransactionQueue::waiting(Address const& _a) const
{
auto it = m_senders.equal_range(_a);
unsigned ret = 0;
for (auto i = it.first; i != it.second; ++i, ++ret) {}
return ret;
}
void TransactionQueue::setFuture(std::pair<h256, Transaction> const& _t) void TransactionQueue::setFuture(std::pair<h256, Transaction> const& _t)
{ {
// cdebug << "txQ::setFuture" << _t.first;
WriteGuard l(m_lock); WriteGuard l(m_lock);
if (m_current.count(_t.first)) if (m_current.count(_t.first))
{ {
m_unknown.insert(make_pair(_t.second.sender(), _t)); m_future.insert(_t);
m_current.erase(_t.first); m_current.erase(_t.first);
} }
} }
void TransactionQueue::noteGood(std::pair<h256, Transaction> const& _t) void TransactionQueue::noteGood(std::pair<h256, Transaction> const& _t)
{ {
// cdebug << "txQ::noteGood" << _t.first;
WriteGuard l(m_lock); WriteGuard l(m_lock);
auto r = m_unknown.equal_range(_t.second.sender()); auto r = m_senders.equal_range(_t.second.sender());
for (auto it = r.first; it != r.second; ++it) for (auto it = r.first; it != r.second; ++it)
m_current.insert(it->second); {
m_unknown.erase(r.first, r.second); auto fit = m_future.find(it->second);
if (fit != m_future.end())
{
m_current.insert(*fit);
m_future.erase(fit);
}
}
} }
void TransactionQueue::drop(h256 const& _txHash) void TransactionQueue::drop(h256 const& _txHash)
@ -174,13 +241,5 @@ void TransactionQueue::drop(h256 const& _txHash)
m_dropped.insert(_txHash); m_dropped.insert(_txHash);
m_known.erase(_txHash); m_known.erase(_txHash);
if (!removeCurrent_WITH_LOCK(_txHash)) remove_WITH_LOCK(_txHash);
{
for (auto i = m_unknown.begin(); i != m_unknown.end(); ++i)
if (i->second.first == _txHash)
{
m_unknown.erase(i);
break;
}
}
} }

14
libethereum/TransactionQueue.h

@ -55,14 +55,15 @@ public:
void drop(h256 const& _txHash); void drop(h256 const& _txHash);
std::unordered_map<h256, Transaction> transactions() const { ReadGuard l(m_lock); return m_current; } unsigned waiting(Address const& _a) const;
std::pair<unsigned, unsigned> items() const { ReadGuard l(m_lock); return std::make_pair(m_current.size(), m_unknown.size()); } std::unordered_map<h256, Transaction> transactions() const;
std::pair<unsigned, unsigned> items() const { ReadGuard l(m_lock); return std::make_pair(m_current.size(), m_future.size()); }
u256 maxNonce(Address const& _a) const; u256 maxNonce(Address const& _a) const;
void setFuture(std::pair<h256, Transaction> const& _t); void setFuture(std::pair<h256, Transaction> const& _t);
void noteGood(std::pair<h256, Transaction> const& _t); void noteGood(std::pair<h256, Transaction> const& _t);
void clear() { WriteGuard l(m_lock); m_known.clear(); m_current.clear(); m_unknown.clear(); } void clear() { WriteGuard l(m_lock); m_senders.clear(); m_known.clear(); m_current.clear(); m_future.clear(); }
template <class T> Handler onReady(T const& _t) { return m_onReady.add(_t); } template <class T> Handler onReady(T const& _t) { return m_onReady.add(_t); }
private: private:
@ -70,15 +71,16 @@ private:
ImportResult manageImport_WITH_LOCK(h256 const& _h, Transaction const& _transaction, ImportCallback const& _cb); ImportResult manageImport_WITH_LOCK(h256 const& _h, Transaction const& _transaction, ImportCallback const& _cb);
void insertCurrent_WITH_LOCK(std::pair<h256, Transaction> const& _p); void insertCurrent_WITH_LOCK(std::pair<h256, Transaction> const& _p);
bool removeCurrent_WITH_LOCK(h256 const& _txHash); bool remove_WITH_LOCK(h256 const& _txHash);
u256 maxNonce_WITH_LOCK(Address const& _a) const;
mutable SharedMutex m_lock; ///< General lock. mutable SharedMutex m_lock; ///< General lock.
h256Hash m_known; ///< Hashes of transactions in both sets. h256Hash m_known; ///< Hashes of transactions in both sets.
std::unordered_multimap<Address, h256> m_senders; ///< Mapping from the sender address to the transaction hash; useful for determining the nonce of a given sender.
std::unordered_map<h256, Transaction> m_current; ///< Map of SHA3(tx) to tx. std::unordered_map<h256, Transaction> m_current; ///< Map of SHA3(tx) to tx.
std::unordered_multimap<Address, std::pair<h256, Transaction>> m_unknown; ///< For transactions that have a future nonce; we map their sender address to the tx stuff, and insert once the sender has a valid TX. std::unordered_map<h256, Transaction> m_future; ///< For transactions that have a future nonce; we re-insert into current once the sender has a valid TX.
std::unordered_map<h256, std::function<void(ImportResult)>> m_callbacks; ///< Called once. std::unordered_map<h256, std::function<void(ImportResult)>> m_callbacks; ///< Called once.
h256Hash m_dropped; ///< Transactions that have previously been dropped. h256Hash m_dropped; ///< Transactions that have previously been dropped.
std::multimap<Address, h256> m_senders; ///< Mapping from the sender address to the transaction hash; useful for determining the nonce of a given sender.
Signal m_onReady; ///< Called when a subsequent call to import transactions will return a non-empty container. Be nice and exit fast. Signal m_onReady; ///< Called when a subsequent call to import transactions will return a non-empty container. Be nice and exit fast.
}; };

16
libevmasm/Assembly.cpp

@ -121,13 +121,13 @@ ostream& Assembly::streamAsm(ostream& _out, string const& _prefix, StringMap con
_out << " " << instructionInfo(i.instruction()).name << "\t" << i.getJumpTypeAsString(); _out << " " << instructionInfo(i.instruction()).name << "\t" << i.getJumpTypeAsString();
break; break;
case Push: case Push:
_out << " PUSH " << i.data(); _out << " PUSH " << hex << i.data();
break; break;
case PushString: case PushString:
_out << " PUSH \"" << m_strings.at((h256)i.data()) << "\""; _out << " PUSH \"" << m_strings.at((h256)i.data()) << "\"";
break; break;
case PushTag: case PushTag:
_out << " PUSH [tag" << i.data() << "]"; _out << " PUSH [tag" << dec << i.data() << "]";
break; break;
case PushSub: case PushSub:
_out << " PUSH [$" << h256(i.data()).abridged() << "]"; _out << " PUSH [$" << h256(i.data()).abridged() << "]";
@ -139,7 +139,7 @@ ostream& Assembly::streamAsm(ostream& _out, string const& _prefix, StringMap con
_out << " PUSHSIZE"; _out << " PUSHSIZE";
break; break;
case Tag: case Tag:
_out << "tag" << i.data() << ": " << endl << _prefix << " JUMPDEST"; _out << "tag" << dec << i.data() << ": " << endl << _prefix << " JUMPDEST";
break; break;
case PushData: case PushData:
_out << " PUSH [" << hex << (unsigned)i.data() << "]"; _out << " PUSH [" << hex << (unsigned)i.data() << "]";
@ -208,7 +208,7 @@ Json::Value Assembly::streamAsmJson(ostream& _out, StringMap const& _sourceCodes
break; break;
case PushTag: case PushTag:
collection.append( collection.append(
createJsonValue("PUSH [tag]", i.getLocation().start, i.getLocation().end, toStringInHex(i.data()))); createJsonValue("PUSH [tag]", i.getLocation().start, i.getLocation().end, string(i.data())));
break; break;
case PushSub: case PushSub:
collection.append( collection.append(
@ -223,19 +223,13 @@ Json::Value Assembly::streamAsmJson(ostream& _out, StringMap const& _sourceCodes
createJsonValue("PUSHSIZE", i.getLocation().start, i.getLocation().end)); createJsonValue("PUSHSIZE", i.getLocation().start, i.getLocation().end));
break; break;
case Tag: case Tag:
{
collection.append( collection.append(
createJsonValue("tag", i.getLocation().start, i.getLocation().end, string(i.data()))); createJsonValue("tag", i.getLocation().start, i.getLocation().end, string(i.data())));
collection.append( collection.append(
createJsonValue("JUMDEST", i.getLocation().start, i.getLocation().end)); createJsonValue("JUMDEST", i.getLocation().start, i.getLocation().end));
}
break; break;
case PushData: case PushData:
{ collection.append(createJsonValue("PUSH data", i.getLocation().start, i.getLocation().end, toStringInHex(i.data())));
Json::Value pushData;
pushData["name"] = "PUSH hex";
collection.append(createJsonValue("PUSH hex", i.getLocation().start, i.getLocation().end, toStringInHex(i.data())));
}
break; break;
default: default:
BOOST_THROW_EXCEPTION(InvalidOpcode()); BOOST_THROW_EXCEPTION(InvalidOpcode());

7
libevmasm/CommonSubexpressionEliminator.cpp

@ -153,7 +153,9 @@ AssemblyItems CSECodeGenerator::generateCode(
assertThrow(!m_classPositions[targetItem.second].empty(), OptimizerException, ""); assertThrow(!m_classPositions[targetItem.second].empty(), OptimizerException, "");
if (m_classPositions[targetItem.second].count(targetItem.first)) if (m_classPositions[targetItem.second].count(targetItem.first))
continue; continue;
SourceLocation const& location = m_expressionClasses.representative(targetItem.second).item->getLocation(); SourceLocation location;
if (m_expressionClasses.representative(targetItem.second).item)
location = m_expressionClasses.representative(targetItem.second).item->getLocation();
int position = classElementPosition(targetItem.second); int position = classElementPosition(targetItem.second);
if (position < targetItem.first) if (position < targetItem.first)
// it is already at its target, we need another copy // it is already at its target, we need another copy
@ -197,7 +199,7 @@ void CSECodeGenerator::addDependencies(Id _c)
addDependencies(argument); addDependencies(argument);
m_neededBy.insert(make_pair(argument, _c)); m_neededBy.insert(make_pair(argument, _c));
} }
if (expr.item->type() == Operation && ( if (expr.item && expr.item->type() == Operation && (
expr.item->instruction() == Instruction::SLOAD || expr.item->instruction() == Instruction::SLOAD ||
expr.item->instruction() == Instruction::MLOAD || expr.item->instruction() == Instruction::MLOAD ||
expr.item->instruction() == Instruction::SHA3 expr.item->instruction() == Instruction::SHA3
@ -288,6 +290,7 @@ void CSECodeGenerator::generateClassElement(Id _c, bool _allowSequenced)
OptimizerException, OptimizerException,
"Sequence constrained operation requested out of sequence." "Sequence constrained operation requested out of sequence."
); );
assertThrow(expr.item, OptimizerException, "Non-generated expression without item.");
vector<Id> const& arguments = expr.arguments; vector<Id> const& arguments = expr.arguments;
for (Id arg: boost::adaptors::reverse(arguments)) for (Id arg: boost::adaptors::reverse(arguments))
generateClassElement(arg); generateClassElement(arg);

51
libevmasm/ControlFlowGraph.cpp

@ -24,6 +24,7 @@
#include <libevmasm/ControlFlowGraph.h> #include <libevmasm/ControlFlowGraph.h>
#include <map> #include <map>
#include <memory> #include <memory>
#include <algorithm>
#include <libevmasm/Exceptions.h> #include <libevmasm/Exceptions.h>
#include <libevmasm/AssemblyItem.h> #include <libevmasm/AssemblyItem.h>
#include <libevmasm/SemanticInformation.h> #include <libevmasm/SemanticInformation.h>
@ -217,7 +218,6 @@ void ControlFlowGraph::gatherKnowledge()
// @todo actually we know that memory is filled with zeros at the beginning, // @todo actually we know that memory is filled with zeros at the beginning,
// we could make use of that. // we could make use of that.
KnownStatePointer emptyState = make_shared<KnownState>(); KnownStatePointer emptyState = make_shared<KnownState>();
ExpressionClasses& expr = emptyState->expressionClasses();
bool unknownJumpEncountered = false; bool unknownJumpEncountered = false;
vector<pair<BlockId, KnownStatePointer>> workQueue({make_pair(BlockId::initial(), emptyState->copy())}); vector<pair<BlockId, KnownStatePointer>> workQueue({make_pair(BlockId::initial(), emptyState->copy())});
@ -238,8 +238,6 @@ void ControlFlowGraph::gatherKnowledge()
} }
block.startState = state->copy(); block.startState = state->copy();
//@todo we might know the return address for the first pass, but not anymore for the second,
// -> store knowledge about tags as a union.
// Feed all items except for the final jump yet because it will erase the target tag. // Feed all items except for the final jump yet because it will erase the target tag.
unsigned pc = block.begin; unsigned pc = block.begin;
@ -254,22 +252,29 @@ void ControlFlowGraph::gatherKnowledge()
assertThrow(block.begin <= pc && pc == block.end - 1, OptimizerException, ""); assertThrow(block.begin <= pc && pc == block.end - 1, OptimizerException, "");
//@todo in the case of JUMPI, add knowledge about the condition to the state //@todo in the case of JUMPI, add knowledge about the condition to the state
// (for both values of the condition) // (for both values of the condition)
BlockId nextBlock = expressionClassToBlockId( set<u256> tags = state->tagsInExpression(
state->stackElement(state->stackHeight(), SourceLocation()), state->stackElement(state->stackHeight(), SourceLocation())
expr
); );
state->feedItem(m_items.at(pc++)); state->feedItem(m_items.at(pc++));
if (nextBlock)
workQueue.push_back(make_pair(nextBlock, state->copy())); if (tags.empty() || std::any_of(tags.begin(), tags.end(), [&](u256 const& _tag)
else if (!unknownJumpEncountered) {
return !m_blocks.count(BlockId(_tag));
}))
{ {
// We do not know where this jump goes, so we have to reset the states of all if (!unknownJumpEncountered)
// JUMPDESTs. {
unknownJumpEncountered = true; // We do not know the target of this jump, so we have to reset the states of all
for (auto const& it: m_blocks) // JUMPDESTs.
if (it.second.begin < it.second.end && m_items[it.second.begin].type() == Tag) unknownJumpEncountered = true;
workQueue.push_back(make_pair(it.first, emptyState->copy())); for (auto const& it: m_blocks)
if (it.second.begin < it.second.end && m_items[it.second.begin].type() == Tag)
workQueue.push_back(make_pair(it.first, emptyState->copy()));
}
} }
else
for (auto tag: tags)
workQueue.push_back(make_pair(BlockId(tag), state->copy()));
} }
else if (block.begin <= pc && pc < block.end) else if (block.begin <= pc && pc < block.end)
state->feedItem(m_items.at(pc++)); state->feedItem(m_items.at(pc++));
@ -329,7 +334,11 @@ BasicBlocks ControlFlowGraph::rebuildCode()
if (previousHandedOver && !pushes[blockId] && m_items[block.begin].type() == Tag) if (previousHandedOver && !pushes[blockId] && m_items[block.begin].type() == Tag)
++block.begin; ++block.begin;
if (block.begin < block.end) if (block.begin < block.end)
{
blocks.push_back(block); blocks.push_back(block);
blocks.back().startState->clearTagUnions();
blocks.back().endState->clearTagUnions();
}
previousHandedOver = (block.endType == BasicBlock::EndType::HANDOVER); previousHandedOver = (block.endType == BasicBlock::EndType::HANDOVER);
} }
} }
@ -337,18 +346,6 @@ BasicBlocks ControlFlowGraph::rebuildCode()
return blocks; return blocks;
} }
BlockId ControlFlowGraph::expressionClassToBlockId(
ExpressionClasses::Id _id,
ExpressionClasses& _exprClasses
)
{
ExpressionClasses::Expression expr = _exprClasses.representative(_id);
if (expr.item && expr.item->type() == PushTag)
return BlockId(expr.item->data());
else
return BlockId::invalid();
}
BlockId ControlFlowGraph::generateNewId() BlockId ControlFlowGraph::generateNewId()
{ {
BlockId id = BlockId(++m_lastUsedId); BlockId id = BlockId(++m_lastUsedId);

4
libevmasm/ControlFlowGraph.h

@ -108,10 +108,6 @@ private:
void setPrevLinks(); void setPrevLinks();
BasicBlocks rebuildCode(); BasicBlocks rebuildCode();
/// @returns the corresponding BlockId if _id is a pushed jump tag,
/// and an invalid BlockId otherwise.
BlockId expressionClassToBlockId(ExpressionClasses::Id _id, ExpressionClasses& _exprClasses);
BlockId generateNewId(); BlockId generateNewId();
unsigned m_lastUsedId = 0; unsigned m_lastUsedId = 0;

10
libevmasm/ExpressionClasses.cpp

@ -82,6 +82,16 @@ ExpressionClasses::Id ExpressionClasses::find(
return exp.id; return exp.id;
} }
ExpressionClasses::Id ExpressionClasses::newClass(SourceLocation const& _location)
{
Expression exp;
exp.id = m_representatives.size();
exp.item = storeItem(AssemblyItem(UndefinedItem, (u256(1) << 255) + exp.id, _location));
m_representatives.push_back(exp);
m_expressions.insert(exp);
return exp.id;
}
bool ExpressionClasses::knownToBeDifferent(ExpressionClasses::Id _a, ExpressionClasses::Id _b) bool ExpressionClasses::knownToBeDifferent(ExpressionClasses::Id _a, ExpressionClasses::Id _b)
{ {
// Try to simplify "_a - _b" and return true iff the value is a non-zero constant. // Try to simplify "_a - _b" and return true iff the value is a non-zero constant.

6
libevmasm/ExpressionClasses.h

@ -52,7 +52,8 @@ public:
Id id; Id id;
AssemblyItem const* item = nullptr; AssemblyItem const* item = nullptr;
Ids arguments; Ids arguments;
unsigned sequenceNumber; ///< Storage modification sequence, only used for SLOAD/SSTORE instructions. /// Storage modification sequence, only used for storage and memory operations.
unsigned sequenceNumber = 0;
/// Behaves as if this was a tuple of (item->type(), item->data(), arguments, sequenceNumber). /// Behaves as if this was a tuple of (item->type(), item->data(), arguments, sequenceNumber).
bool operator<(Expression const& _other) const; bool operator<(Expression const& _other) const;
}; };
@ -73,6 +74,9 @@ public:
/// @returns the number of classes. /// @returns the number of classes.
Id size() const { return m_representatives.size(); } Id size() const { return m_representatives.size(); }
/// @returns the id of a new class which is different to all other classes.
Id newClass(SourceLocation const& _location);
/// @returns true if the values of the given classes are known to be different (on every input). /// @returns true if the values of the given classes are known to be different (on every input).
/// @note that this function might still return false for some different inputs. /// @note that this function might still return false for some different inputs.
bool knownToBeDifferent(Id _a, Id _b); bool knownToBeDifferent(Id _a, Id _b);

92
libevmasm/KnownState.cpp

@ -162,29 +162,41 @@ KnownState::StoreOperation KnownState::feedItem(AssemblyItem const& _item, bool
/// Helper function for KnownState::reduceToCommonKnowledge, removes everything from /// Helper function for KnownState::reduceToCommonKnowledge, removes everything from
/// _this which is not in or not equal to the value in _other. /// _this which is not in or not equal to the value in _other.
template <class _Mapping, class _KeyType> void intersect( template <class _Mapping> void intersect(_Mapping& _this, _Mapping const& _other)
_Mapping& _this,
_Mapping const& _other,
function<_KeyType(_KeyType)> const& _keyTrans = [](_KeyType _k) { return _k; }
)
{ {
for (auto it = _this.begin(); it != _this.end();) for (auto it = _this.begin(); it != _this.end();)
if (_other.count(_keyTrans(it->first)) && _other.at(_keyTrans(it->first)) == it->second) if (_other.count(it->first) && _other.at(it->first) == it->second)
++it; ++it;
else else
it = _this.erase(it); it = _this.erase(it);
} }
template <class _Mapping> void intersect(_Mapping& _this, _Mapping const& _other)
{
intersect<_Mapping, ExpressionClasses::Id>(_this, _other, [](ExpressionClasses::Id _k) { return _k; });
}
void KnownState::reduceToCommonKnowledge(KnownState const& _other) void KnownState::reduceToCommonKnowledge(KnownState const& _other)
{ {
int stackDiff = m_stackHeight - _other.m_stackHeight; int stackDiff = m_stackHeight - _other.m_stackHeight;
function<int(int)> stackKeyTransform = [=](int _key) -> int { return _key - stackDiff; }; for (auto it = m_stackElements.begin(); it != m_stackElements.end();)
intersect(m_stackElements, _other.m_stackElements, stackKeyTransform); if (_other.m_stackElements.count(it->first - stackDiff))
{
Id other = _other.m_stackElements.at(it->first - stackDiff);
if (it->second == other)
++it;
else
{
set<u256> theseTags = tagsInExpression(it->second);
set<u256> otherTags = tagsInExpression(other);
if (!theseTags.empty() && !otherTags.empty())
{
theseTags.insert(otherTags.begin(), otherTags.end());
it->second = tagUnion(theseTags);
++it;
}
else
it = m_stackElements.erase(it);
}
}
else
it = m_stackElements.erase(it);
// Use the smaller stack height. Essential to terminate in case of loops. // Use the smaller stack height. Essential to terminate in case of loops.
if (m_stackHeight > _other.m_stackHeight) if (m_stackHeight > _other.m_stackHeight)
{ {
@ -201,10 +213,15 @@ void KnownState::reduceToCommonKnowledge(KnownState const& _other)
bool KnownState::operator==(const KnownState& _other) const bool KnownState::operator==(const KnownState& _other) const
{ {
return m_storageContent == _other.m_storageContent && if (m_storageContent != _other.m_storageContent || m_memoryContent != _other.m_memoryContent)
m_memoryContent == _other.m_memoryContent && return false;
m_stackHeight == _other.m_stackHeight && int stackDiff = m_stackHeight - _other.m_stackHeight;
m_stackElements == _other.m_stackElements; auto thisIt = m_stackElements.cbegin();
auto otherIt = _other.m_stackElements.cbegin();
for (; thisIt != m_stackElements.cend() && otherIt != _other.m_stackElements.cend(); ++thisIt, ++otherIt)
if (thisIt->first - stackDiff != otherIt->first || thisIt->second != otherIt->second)
return false;
return (thisIt == m_stackElements.cend() && otherIt == _other.m_stackElements.cend());
} }
ExpressionClasses::Id KnownState::stackElement(int _stackHeight, SourceLocation const& _location) ExpressionClasses::Id KnownState::stackElement(int _stackHeight, SourceLocation const& _location)
@ -212,18 +229,17 @@ ExpressionClasses::Id KnownState::stackElement(int _stackHeight, SourceLocation
if (m_stackElements.count(_stackHeight)) if (m_stackElements.count(_stackHeight))
return m_stackElements.at(_stackHeight); return m_stackElements.at(_stackHeight);
// Stack element not found (not assigned yet), create new unknown equivalence class. // Stack element not found (not assigned yet), create new unknown equivalence class.
//@todo check that we do not infer incorrect equivalences when the stack is cleared partially return m_stackElements[_stackHeight] =
//in between. m_expressionClasses->find(AssemblyItem(UndefinedItem, _stackHeight, _location));
return m_stackElements[_stackHeight] = initialStackElement(_stackHeight, _location);
} }
ExpressionClasses::Id KnownState::initialStackElement( void KnownState::clearTagUnions()
int _stackHeight,
SourceLocation const& _location
)
{ {
// This is a special assembly item that refers to elements pre-existing on the initial stack. for (auto it = m_stackElements.begin(); it != m_stackElements.end();)
return m_expressionClasses->find(AssemblyItem(UndefinedItem, u256(_stackHeight), _location)); if (m_tagUnions.left.count(it->second))
it = m_stackElements.erase(it);
else
++it;
} }
void KnownState::setStackElement(int _stackHeight, Id _class) void KnownState::setStackElement(int _stackHeight, Id _class)
@ -352,3 +368,27 @@ KnownState::Id KnownState::applySha3(
return m_knownSha3Hashes[arguments] = v; return m_knownSha3Hashes[arguments] = v;
} }
set<u256> KnownState::tagsInExpression(KnownState::Id _expressionId)
{
if (m_tagUnions.left.count(_expressionId))
return m_tagUnions.left.at(_expressionId);
// Might be a tag, then return the set of itself.
ExpressionClasses::Expression expr = m_expressionClasses->representative(_expressionId);
if (expr.item && expr.item->type() == PushTag)
return set<u256>({expr.item->data()});
else
return set<u256>();
}
KnownState::Id KnownState::tagUnion(set<u256> _tags)
{
if (m_tagUnions.right.count(_tags))
return m_tagUnions.right.at(_tags);
else
{
Id id = m_expressionClasses->newClass(SourceLocation());
m_tagUnions.right.insert(make_pair(_tags, id));
return id;
}
}

19
libevmasm/KnownState.h

@ -29,6 +29,7 @@
#include <tuple> #include <tuple>
#include <memory> #include <memory>
#include <ostream> #include <ostream>
#include <boost/bimap.hpp>
#include <libdevcore/CommonIO.h> #include <libdevcore/CommonIO.h>
#include <libdevcore/Exceptions.h> #include <libdevcore/Exceptions.h>
#include <libevmasm/ExpressionClasses.h> #include <libevmasm/ExpressionClasses.h>
@ -107,15 +108,16 @@ public:
/// @returns true if the knowledge about the state of both objects is (known to be) equal. /// @returns true if the knowledge about the state of both objects is (known to be) equal.
bool operator==(KnownState const& _other) const; bool operator==(KnownState const& _other) const;
///@todo the sequence numbers in two copies of this class should never be the same.
/// might be doable using two-dimensional sequence numbers, where the first value is incremented
/// for each copy
/// Retrieves the current equivalence class fo the given stack element (or generates a new /// Retrieves the current equivalence class fo the given stack element (or generates a new
/// one if it does not exist yet). /// one if it does not exist yet).
Id stackElement(int _stackHeight, SourceLocation const& _location); Id stackElement(int _stackHeight, SourceLocation const& _location);
/// @returns the equivalence class id of the special initial stack element at the given height.
Id initialStackElement(int _stackHeight, SourceLocation const& _location); /// @returns its set of tags if the given expression class is a known tag union; returns a set
/// containing the tag if it is a PushTag expression and the empty set otherwise.
std::set<u256> tagsInExpression(Id _expressionId);
/// During analysis, different tags on the stack are partially treated as the same class.
/// This removes such classes not to confuse later analyzers.
void clearTagUnions();
int stackHeight() const { return m_stackHeight; } int stackHeight() const { return m_stackHeight; }
std::map<int, Id> const& stackElements() const { return m_stackElements; } std::map<int, Id> const& stackElements() const { return m_stackElements; }
@ -142,6 +144,9 @@ private:
/// Finds or creates a new expression that applies the sha3 hash function to the contents in memory. /// Finds or creates a new expression that applies the sha3 hash function to the contents in memory.
Id applySha3(Id _start, Id _length, SourceLocation const& _location); Id applySha3(Id _start, Id _length, SourceLocation const& _location);
/// @returns a new or already used Id representing the given set of tags.
Id tagUnion(std::set<u256> _tags);
/// Current stack height, can be negative. /// Current stack height, can be negative.
int m_stackHeight = 0; int m_stackHeight = 0;
/// Current stack layout, mapping stack height -> equivalence class /// Current stack layout, mapping stack height -> equivalence class
@ -157,6 +162,8 @@ private:
std::map<std::vector<Id>, Id> m_knownSha3Hashes; std::map<std::vector<Id>, Id> m_knownSha3Hashes;
/// Structure containing the classes of equivalent expressions. /// Structure containing the classes of equivalent expressions.
std::shared_ptr<ExpressionClasses> m_expressionClasses; std::shared_ptr<ExpressionClasses> m_expressionClasses;
/// Container for unions of tags stored on the stack.
boost::bimap<Id, std::set<u256>> m_tagUnions;
}; };
} }

5
libjsconsole/JSConsole.cpp

@ -25,7 +25,6 @@
#include <libweb3jsonrpc/WebThreeStubServer.h> #include <libweb3jsonrpc/WebThreeStubServer.h>
#include "JSConsole.h" #include "JSConsole.h"
#include "JSV8Connector.h" #include "JSV8Connector.h"
#include "libjsconsole/JSConsoleResources.hpp"
// TODO! make readline optional! // TODO! make readline optional!
#include <readline/readline.h> #include <readline/readline.h>
@ -35,12 +34,12 @@ using namespace std;
using namespace dev; using namespace dev;
using namespace dev::eth; using namespace dev::eth;
JSConsole::JSConsole(WebThreeDirect& _web3, std::vector<dev::KeyPair> const& _accounts): JSConsole::JSConsole(WebThreeDirect& _web3, shared_ptr<AccountHolder> const& _accounts):
m_engine(), m_engine(),
m_printer(m_engine) m_printer(m_engine)
{ {
m_jsonrpcConnector.reset(new JSV8Connector(m_engine)); m_jsonrpcConnector.reset(new JSV8Connector(m_engine));
m_jsonrpcServer.reset(new WebThreeStubServer(*m_jsonrpcConnector.get(), _web3, _accounts)); m_jsonrpcServer.reset(new WebThreeStubServer(*m_jsonrpcConnector.get(), _web3, _accounts, vector<KeyPair>()));
} }
JSConsole::~JSConsole() {} JSConsole::~JSConsole() {}

4
libjsconsole/JSConsole.h

@ -33,10 +33,12 @@ namespace dev
namespace eth namespace eth
{ {
class AccountHolder;
class JSConsole class JSConsole
{ {
public: public:
JSConsole(WebThreeDirect& _web3, std::vector<dev::KeyPair> const& _accounts); JSConsole(WebThreeDirect& _web3, std::shared_ptr<AccountHolder> const& _accounts);
~JSConsole(); ~JSConsole();
void repl() const; void repl() const;

2
libjsqrc/ethereumjs/bower.json

@ -1,7 +1,7 @@
{ {
"name": "web3", "name": "web3",
"namespace": "ethereum", "namespace": "ethereum",
"version": "0.3.6", "version": "0.4.2",
"description": "Ethereum Compatible JavaScript API", "description": "Ethereum Compatible JavaScript API",
"main": [ "main": [
"./dist/web3.js", "./dist/web3.js",

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

@ -15,52 +15,6 @@ require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof requ
You should have received a copy of the GNU Lesser General Public License 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/>. along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
*/ */
/**
* @file abi.js
* @author Marek Kotewicz <marek@ethdev.com>
* @author Gav Wood <g@ethdev.com>
* @date 2014
*/
var coder = require('./coder');
var utils = require('./utils');
var formatConstructorParams = function (abi, params) {
var constructor = utils.getConstructor(abi, params.length);
if (!constructor) {
if (params.length > 0) {
console.warn("didn't found matching constructor, using default one");
}
return '';
}
return coder.encodeParams(constructor.inputs.map(function (input) {
return input.type;
}), params);
};
module.exports = {
formatConstructorParams: formatConstructorParams
};
},{"./coder":2,"./utils":5}],2:[function(require,module,exports){
/*
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 coder.js * @file coder.js
* @author Marek Kotewicz <marek@ethdev.com> * @author Marek Kotewicz <marek@ethdev.com>
@ -328,7 +282,7 @@ var coder = new SolidityCoder([
module.exports = coder; module.exports = coder;
},{"../utils/utils":8,"./formatters":3,"./param":4,"bignumber.js":"bignumber.js"}],3:[function(require,module,exports){ },{"../utils/utils":6,"./formatters":2,"./param":3,"bignumber.js":"bignumber.js"}],2:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -548,7 +502,7 @@ module.exports = {
}; };
},{"../utils/config":7,"../utils/utils":8,"./param":4,"bignumber.js":"bignumber.js"}],4:[function(require,module,exports){ },{"../utils/config":5,"../utils/utils":6,"./param":3,"bignumber.js":"bignumber.js"}],3:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -760,54 +714,7 @@ SolidityParam.decodeArray = function (bytes, index) {
module.exports = SolidityParam; module.exports = SolidityParam;
},{"../utils/utils":8}],5:[function(require,module,exports){ },{"../utils/utils":6}],4:[function(require,module,exports){
/*
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 utils.js
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
*/
/**
* Returns the contstructor with matching number of arguments
*
* @method getConstructor
* @param {Array} abi
* @param {Number} numberOfArgs
* @returns {Object} constructor function abi
*/
var getConstructor = function (abi, numberOfArgs) {
return abi.filter(function (f) {
return f.type === 'constructor' && f.inputs.length === numberOfArgs;
})[0];
};
//var getSupremeType = function (type) {
//return type.substr(0, type.indexOf('[')) + ']';
//};
module.exports = {
getConstructor: getConstructor
};
},{}],6:[function(require,module,exports){
'use strict'; 'use strict';
// go env doesn't have and need XMLHttpRequest // go env doesn't have and need XMLHttpRequest
@ -818,7 +725,7 @@ if (typeof XMLHttpRequest === 'undefined') {
} }
},{}],7:[function(require,module,exports){ },{}],5:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -890,7 +797,7 @@ module.exports = {
}; };
},{"bignumber.js":"bignumber.js"}],8:[function(require,module,exports){ },{"bignumber.js":"bignumber.js"}],6:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -1256,6 +1163,7 @@ var toAddress = function (address) {
return '0x' + padLeft(toHex(address).substr(2), 40); return '0x' + padLeft(toHex(address).substr(2), 40);
}; };
/** /**
* Returns true if object is BigNumber, otherwise false * Returns true if object is BigNumber, otherwise false
* *
@ -1366,12 +1274,12 @@ module.exports = {
}; };
},{"bignumber.js":"bignumber.js"}],9:[function(require,module,exports){ },{"bignumber.js":"bignumber.js"}],7:[function(require,module,exports){
module.exports={ module.exports={
"version": "0.3.6" "version": "0.4.2"
} }
},{}],10:[function(require,module,exports){ },{}],8:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -1411,6 +1319,7 @@ var RequestManager = require('./web3/requestmanager');
var c = require('./utils/config'); var c = require('./utils/config');
var Method = require('./web3/method'); var Method = require('./web3/method');
var Property = require('./web3/property'); var Property = require('./web3/property');
var Batch = require('./web3/batch');
var web3Methods = [ var web3Methods = [
new Method({ new Method({
@ -1503,6 +1412,9 @@ 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.createBatch = function () {
return new Batch();
};
// ADD defaultblock // ADD defaultblock
Object.defineProperty(web3.eth, 'defaultBlock', { Object.defineProperty(web3.eth, 'defaultBlock', {
@ -1538,7 +1450,70 @@ setupMethods(web3.shh, shh.methods);
module.exports = web3; module.exports = web3;
},{"./utils/config":7,"./utils/utils":8,"./version.json":9,"./web3/db":12,"./web3/eth":14,"./web3/filter":16,"./web3/formatters":17,"./web3/method":21,"./web3/net":22,"./web3/property":23,"./web3/requestmanager":25,"./web3/shh":26,"./web3/watches":27}],11:[function(require,module,exports){ },{"./utils/config":5,"./utils/utils":6,"./version.json":7,"./web3/batch":9,"./web3/db":11,"./web3/eth":13,"./web3/filter":15,"./web3/formatters":16,"./web3/method":20,"./web3/net":21,"./web3/property":22,"./web3/requestmanager":24,"./web3/shh":25,"./web3/watches":26}],9:[function(require,module,exports){
/*
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 batch.js
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
*/
var RequestManager = require('./requestmanager');
var Batch = function () {
this.requests = [];
};
/**
* Should be called to add create new request to batch request
*
* @method add
* @param {Object} jsonrpc requet object
*/
Batch.prototype.add = function (request) {
this.requests.push(request);
};
/**
* Should be called to execute batch request
*
* @method execute
*/
Batch.prototype.execute = function () {
var requests = this.requests;
RequestManager.getInstance().sendBatch(requests, function (err, results) {
results = results || [];
requests.map(function (request, index) {
return results[index] || {};
}).map(function (result, index) {
return requests[index].format ? requests[index].format(result.result) : result.result;
}).forEach(function (result, index) {
if (requests[index].callback) {
requests[index].callback(err, result);
}
});
});
};
module.exports = Batch;
},{"./requestmanager":24}],10:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -1562,13 +1537,39 @@ module.exports = web3;
*/ */
var web3 = require('../web3'); var web3 = require('../web3');
var solAbi = require('../solidity/abi');
var utils = require('../utils/utils'); var utils = require('../utils/utils');
var coder = require('../solidity/coder');
var SolidityEvent = require('./event'); var SolidityEvent = require('./event');
var SolidityFunction = require('./function'); var SolidityFunction = require('./function');
var addFunctionsToContract = function (contract, desc) { /**
desc.filter(function (json) { * Should be called to encode constructor params
*
* @method encodeConstructorParams
* @param {Array} abi
* @param {Array} constructor params
*/
var encodeConstructorParams = function (abi, params) {
return abi.filter(function (json) {
return json.type === 'constructor' && json.inputs.length === params.length;
}).map(function (json) {
return json.inputs.map(function (input) {
return input.type;
});
}).map(function (types) {
return coder.encodeParams(types, params);
})[0] || '';
};
/**
* Should be called to add functions to contract object
*
* @method addFunctionsToContract
* @param {Contract} contract
* @param {Array} abi
*/
var addFunctionsToContract = function (contract, abi) {
abi.filter(function (json) {
return json.type === 'function'; return json.type === 'function';
}).map(function (json) { }).map(function (json) {
return new SolidityFunction(json, contract.address); return new SolidityFunction(json, contract.address);
@ -1577,8 +1578,15 @@ var addFunctionsToContract = function (contract, desc) {
}); });
}; };
var addEventsToContract = function (contract, desc) { /**
desc.filter(function (json) { * Should be called to add events to contract object
*
* @method addEventsToContract
* @param {Contract} contract
* @param {Array} abi
*/
var addEventsToContract = function (contract, abi) {
abi.filter(function (json) {
return json.type === 'event'; return json.type === 'event';
}).map(function (json) { }).map(function (json) {
return new SolidityEvent(json, contract.address); return new SolidityEvent(json, contract.address);
@ -1588,65 +1596,106 @@ var addEventsToContract = function (contract, desc) {
}; };
/** /**
* This method should be called when we want to call / transact some solidity method from javascript * Should be called to create new ContractFactory
* it returns an object which has same methods available as solidity contract description
* usage example:
*
* var abi = [{
* name: 'myMethod',
* inputs: [{ name: 'a', type: 'string' }],
* outputs: [{name: 'd', type: 'string' }]
* }]; // contract abi
*
* var MyContract = web3.eth.contract(abi); // creation of contract prototype
*
* var contractInstance = new MyContract('0x0123123121');
* *
* contractInstance.myMethod('this is test string param for call'); // myMethod call (implicit, default) * @method contract
* contractInstance.call().myMethod('this is test string param for call'); // myMethod call (explicit) * @param {Array} abi
* contractInstance.sendTransaction().myMethod('this is test string param for transact'); // myMethod sendTransaction * @returns {ContractFactory} new contract factory
*
* @param abi - abi json description of the contract, which is being created
* @returns contract object
*/ */
var contract = function (abi) { var contract = function (abi) {
return new ContractFactory(abi);
};
// return prototype /**
return Contract.bind(null, abi); * Should be called to create new ContractFactory instance
*
* @method ContractFactory
* @param {Array} abi
*/
var ContractFactory = function (abi) {
this.abi = abi;
}; };
var Contract = function (abi, options) { /**
* Should be called to create new contract on a blockchain
*
* @method new
* @param {Any} contract constructor param1 (optional)
* @param {Any} contract constructor param2 (optional)
* @param {Object} contract transaction object (required)
* @param {Function} callback
* @returns {Contract} returns contract if no callback was passed,
* otherwise calls callback function (err, contract)
*/
ContractFactory.prototype.new = function () {
// parse arguments
var options = {}; // required!
var callback;
this.address = ''; var args = Array.prototype.slice.call(arguments);
if (utils.isAddress(options)) { if (utils.isFunction(args[args.length - 1])) {
this.address = options; callback = args.pop();
} else { // is an object!
// TODO, parse the rest of the args
options = options || {};
var args = Array.prototype.slice.call(arguments, 2);
var bytes = solAbi.formatConstructorParams(abi, args);
options.data += bytes;
this.address = web3.eth.sendTransaction(options);
} }
addFunctionsToContract(this, abi); var last = args[args.length - 1];
addEventsToContract(this, abi); if (utils.isObject(last) && !utils.isArray(last)) {
options = args.pop();
}
// throw an error if there are no options
var bytes = encodeConstructorParams(this.abi, args);
options.data += bytes;
if (!callback) {
var address = web3.eth.sendTransaction(options);
return this.at(address);
}
var self = this;
web3.eth.sendTransaction(options, function (err, address) {
if (err) {
callback(err);
}
self.at(address, callback);
});
}; };
Contract.prototype.call = function () { /**
console.error('contract.call is deprecated'); * Should be called to get access to existing contract on a blockchain
return this; *
* @method at
* @param {Address} contract address (required)
* @param {Function} callback {optional)
* @returns {Contract} returns contract if no callback was passed,
* otherwise calls callback function (err, contract)
*/
ContractFactory.prototype.at = function (address, callback) {
// TODO: address is required
if (callback) {
callback(null, new Contract(this.abi, address));
}
return new Contract(this.abi, address);
}; };
Contract.prototype.sendTransaction = function () { /**
console.error('contract.sendTransact is deprecated'); * Should be called to create new contract instance
return this; *
* @method Contract
* @param {Array} abi
* @param {Address} contract address
*/
var Contract = function (abi, address) {
this.address = address;
addFunctionsToContract(this, abi);
addEventsToContract(this, abi);
}; };
module.exports = contract; module.exports = contract;
},{"../solidity/abi":1,"../utils/utils":8,"../web3":10,"./event":15,"./function":18}],12:[function(require,module,exports){ },{"../solidity/coder":1,"../utils/utils":6,"../web3":8,"./event":14,"./function":17}],11:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -1704,7 +1753,7 @@ module.exports = {
methods: methods methods: methods
}; };
},{"./method":21}],13:[function(require,module,exports){ },{"./method":20}],12:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -1744,7 +1793,7 @@ module.exports = {
}; };
},{}],14:[function(require,module,exports){ },{}],13:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -1921,6 +1970,14 @@ var call = new Method({
inputFormatter: [formatters.inputTransactionFormatter, formatters.inputDefaultBlockNumberFormatter] inputFormatter: [formatters.inputTransactionFormatter, formatters.inputDefaultBlockNumberFormatter]
}); });
var estimateGas = new Method({
name: 'estimateGas',
call: 'eth_estimateGas',
params: 1,
inputFormatter: [formatters.inputTransactionFormatter],
outputFormatter: utils.toDecimal
});
var compileSolidity = new Method({ var compileSolidity = new Method({
name: 'compile.solidity', name: 'compile.solidity',
call: 'eth_compileSolidity', call: 'eth_compileSolidity',
@ -1939,6 +1996,18 @@ var compileSerpent = new Method({
params: 1 params: 1
}); });
var submitWork = new Method({
name: 'submitWork',
call: 'eth_submitWork',
params: 3
});
var getWork = new Method({
name: 'getWork',
call: 'eth_getWork',
params: 0
});
var methods = [ var methods = [
getBalance, getBalance,
getStorageAt, getStorageAt,
@ -1952,10 +2021,13 @@ var methods = [
getTransactionFromBlock, getTransactionFromBlock,
getTransactionCount, getTransactionCount,
call, call,
estimateGas,
sendTransaction, sendTransaction,
compileSolidity, compileSolidity,
compileLLL, compileLLL,
compileSerpent, compileSerpent,
submitWork,
getWork
]; ];
/// @returns an array of objects describing web3.eth api properties /// @returns an array of objects describing web3.eth api properties
@ -1998,7 +2070,7 @@ module.exports = {
}; };
},{"../utils/utils":8,"./formatters":17,"./method":21,"./property":23}],15:[function(require,module,exports){ },{"../utils/utils":6,"./formatters":16,"./method":20,"./property":22}],14:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -2194,7 +2266,7 @@ SolidityEvent.prototype.attachToContract = function (contract) {
module.exports = SolidityEvent; module.exports = SolidityEvent;
},{"../solidity/coder":2,"../utils/utils":8,"../web3":10,"./formatters":17}],16:[function(require,module,exports){ },{"../solidity/coder":1,"../utils/utils":6,"../web3":8,"./formatters":16}],15:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -2351,7 +2423,7 @@ Filter.prototype.get = function (callback) {
module.exports = Filter; module.exports = Filter;
},{"../utils/utils":8,"./formatters":17,"./requestmanager":25}],17:[function(require,module,exports){ },{"../utils/utils":6,"./formatters":16,"./requestmanager":24}],16:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -2571,7 +2643,7 @@ module.exports = {
}; };
},{"../utils/config":7,"../utils/utils":8}],18:[function(require,module,exports){ },{"../utils/config":5,"../utils/utils":6}],17:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -2588,7 +2660,7 @@ module.exports = {
You should have received a copy of the GNU Lesser General Public License 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/>. along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** /**
* @file function.js * @file function.js
* @author Marek Kotewicz <marek@ethdev.com> * @author Marek Kotewicz <marek@ethdev.com>
* @date 2015 * @date 2015
@ -2613,18 +2685,23 @@ var SolidityFunction = function (json, address) {
this._address = address; this._address = address;
}; };
SolidityFunction.prototype.extractCallback = function (args) {
if (utils.isFunction(args[args.length - 1])) {
return args.pop(); // modify the args array!
}
};
/** /**
* Should be used to create payload from arguments * Should be used to create payload from arguments
* *
* @method toPayload * @method toPayload
* @param {...} solidity function params * @param {Array} solidity function params
* @param {Object} optional payload options * @param {Object} optional payload options
*/ */
SolidityFunction.prototype.toPayload = function () { SolidityFunction.prototype.toPayload = function (args) {
var args = Array.prototype.slice.call(arguments);
var options = {}; var options = {};
if (args.length > this._inputTypes.length && utils.isObject(args[args.length -1])) { if (args.length > this._inputTypes.length && utils.isObject(args[args.length -1])) {
options = args.pop(); options = args[args.length - 1];
} }
options.to = this._address; options.to = this._address;
options.data = '0x' + this.signature() + coder.encodeParams(this._inputTypes, args); options.data = '0x' + this.signature() + coder.encodeParams(this._inputTypes, args);
@ -2641,19 +2718,41 @@ SolidityFunction.prototype.signature = function () {
return web3.sha3(web3.fromAscii(this._name)).slice(2, 10); return web3.sha3(web3.fromAscii(this._name)).slice(2, 10);
}; };
SolidityFunction.prototype.unpackOutput = function (output) {
if (output === null) {
return;
}
output = output.length >= 2 ? output.slice(2) : output;
var result = coder.decodeParams(this._outputTypes, output);
return result.length === 1 ? result[0] : result;
};
/** /**
* Should be used to call function * Calls a contract function.
* *
* @method call * @method call
* @param {Object} options * @param {...Object} Contract function arguments
* @param {function} If the last argument is a function, the contract function
* call will be asynchronous, and the callback will be passed the
* error and result.
* @return {String} output bytes * @return {String} output bytes
*/ */
SolidityFunction.prototype.call = function () { SolidityFunction.prototype.call = function () {
var payload = this.toPayload.apply(this, Array.prototype.slice.call(arguments)); var args = Array.prototype.slice.call(arguments);
var output = web3.eth.call(payload); var callback = this.extractCallback(args);
output = output.length >= 2 ? output.slice(2) : output; var payload = this.toPayload(args);
var result = coder.decodeParams(this._outputTypes, output);
return result.length === 1 ? result[0] : result; if (!callback) {
var output = web3.eth.call(payload);
return this.unpackOutput(output);
}
var self = this;
web3.eth.call(payload, function (error, output) {
callback(error, self.unpackOutput(output));
});
}; };
/** /**
@ -2663,8 +2762,16 @@ SolidityFunction.prototype.call = function () {
* @param {Object} options * @param {Object} options
*/ */
SolidityFunction.prototype.sendTransaction = function () { SolidityFunction.prototype.sendTransaction = function () {
var payload = this.toPayload.apply(this, Array.prototype.slice.call(arguments)); var args = Array.prototype.slice.call(arguments);
web3.eth.sendTransaction(payload); var callback = this.extractCallback(args);
var payload = this.toPayload(args);
if (!callback) {
web3.eth.sendTransaction(payload);
return;
}
web3.eth.sendTransaction(payload, callback);
}; };
/** /**
@ -2679,7 +2786,7 @@ SolidityFunction.prototype.displayName = function () {
/** /**
* Should be used to get function type name * Should be used to get function type name
* *
* @method typeName * @method typeName
* @return {String} type name of the function * @return {String} type name of the function
*/ */
@ -2687,6 +2794,25 @@ SolidityFunction.prototype.typeName = function () {
return utils.extractTypeName(this._name); return utils.extractTypeName(this._name);
}; };
/**
* Should be called to get rpc requests from solidity function
*
* @method request
* @returns {Object}
*/
SolidityFunction.prototype.request = function () {
var args = Array.prototype.slice.call(arguments);
var callback = this.extractCallback(args);
var payload = this.toPayload(args);
var format = this.unpackOutput.bind(this);
return {
callback: callback,
payload: payload,
format: format
};
};
/** /**
* Should be called to execute function * Should be called to execute function
* *
@ -2694,7 +2820,7 @@ SolidityFunction.prototype.typeName = function () {
*/ */
SolidityFunction.prototype.execute = function () { SolidityFunction.prototype.execute = function () {
var transaction = !this._constant; var transaction = !this._constant;
// send transaction // send transaction
if (transaction) { if (transaction) {
return this.sendTransaction.apply(this, Array.prototype.slice.call(arguments)); return this.sendTransaction.apply(this, Array.prototype.slice.call(arguments));
@ -2712,6 +2838,7 @@ SolidityFunction.prototype.execute = function () {
*/ */
SolidityFunction.prototype.attachToContract = function (contract) { SolidityFunction.prototype.attachToContract = function (contract) {
var execute = this.execute.bind(this); var execute = this.execute.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);
var displayName = this.displayName(); var displayName = this.displayName();
@ -2724,7 +2851,7 @@ SolidityFunction.prototype.attachToContract = function (contract) {
module.exports = SolidityFunction; module.exports = SolidityFunction;
},{"../solidity/coder":2,"../utils/utils":8,"../web3":10}],19:[function(require,module,exports){ },{"../solidity/coder":1,"../utils/utils":6,"../web3":8}],18:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -2816,7 +2943,7 @@ HttpProvider.prototype.sendAsync = function (payload, callback) {
module.exports = HttpProvider; module.exports = HttpProvider;
},{"./errors":13,"xmlhttprequest":6}],20:[function(require,module,exports){ },{"./errors":12,"xmlhttprequest":4}],19:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -2909,7 +3036,7 @@ Jsonrpc.prototype.toBatchPayload = function (messages) {
module.exports = Jsonrpc; module.exports = Jsonrpc;
},{}],21:[function(require,module,exports){ },{}],20:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -2966,7 +3093,6 @@ Method.prototype.extractCallback = function (args) {
if (utils.isFunction(args[args.length - 1])) { if (utils.isFunction(args[args.length - 1])) {
return args.pop(); // modify the args array! return args.pop(); // modify the args array!
} }
return null;
}; };
/** /**
@ -3019,6 +3145,7 @@ Method.prototype.formatOutput = function (result) {
*/ */
Method.prototype.attachToObject = function (obj) { Method.prototype.attachToObject = function (obj) {
var func = this.send.bind(this); var func = this.send.bind(this);
func.request = this.request.bind(this);
func.call = this.call; // that's ugly. filter.js uses it func.call = this.call; // that's ugly. filter.js uses it
var name = this.name.split('.'); var name = this.name.split('.');
if (name.length > 1) { if (name.length > 1) {
@ -3049,6 +3176,19 @@ Method.prototype.toPayload = function (args) {
}; };
}; };
/**
* Should be called to create pure JSONRPC request which can be used in batch request
*
* @method request
* @param {...} params
* @return {Object} jsonrpc request
*/
Method.prototype.request = function () {
var payload = this.toPayload(Array.prototype.slice.call(arguments));
payload.format = this.formatOutput.bind(this);
return payload;
};
/** /**
* Should send request to the API * Should send request to the API
* *
@ -3061,7 +3201,7 @@ Method.prototype.send = function () {
if (payload.callback) { if (payload.callback) {
var self = this; var self = this;
return RequestManager.getInstance().sendAsync(payload, function (err, result) { return RequestManager.getInstance().sendAsync(payload, function (err, result) {
payload.callback(null, self.formatOutput(result)); payload.callback(err, self.formatOutput(result));
}); });
} }
return this.formatOutput(RequestManager.getInstance().send(payload)); return this.formatOutput(RequestManager.getInstance().send(payload));
@ -3070,7 +3210,7 @@ Method.prototype.send = function () {
module.exports = Method; module.exports = Method;
},{"../utils/utils":8,"./errors":13,"./requestmanager":25}],22:[function(require,module,exports){ },{"../utils/utils":6,"./errors":12,"./requestmanager":24}],21:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -3120,7 +3260,7 @@ module.exports = {
}; };
},{"../utils/utils":8,"./property":23}],23:[function(require,module,exports){ },{"../utils/utils":6,"./property":22}],22:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -3186,16 +3326,23 @@ Property.prototype.formatOutput = function (result) {
Property.prototype.attachToObject = function (obj) { Property.prototype.attachToObject = function (obj) {
var proto = { var proto = {
get: this.get.bind(this), get: this.get.bind(this),
set: this.set.bind(this)
}; };
var name = this.name.split('.'); var names = this.name.split('.');
if (name.length > 1) { var name = names[0];
obj[name[0]] = obj[name[0]] || {}; if (names.length > 1) {
Object.defineProperty(obj[name[0]], name[1], proto); obj[names[0]] = obj[names[0]] || {};
} else { obj = obj[names[0]];
Object.defineProperty(obj, name[0], proto); name = names[1];
} }
Object.defineProperty(obj, name, proto);
var toAsyncName = function (prefix, name) {
return prefix + name.charAt(0).toUpperCase() + name.slice(1);
};
obj[toAsyncName('get', name)] = this.getAsync.bind(this);
}; };
/** /**
@ -3211,22 +3358,27 @@ Property.prototype.get = function () {
}; };
/** /**
* Should be used to set value of the property * Should be used to asynchrounously get value of property
* *
* @method set * @method getAsync
* @param {Object} new value of the property * @param {Function}
*/ */
Property.prototype.set = function (value) { Property.prototype.getAsync = function (callback) {
return RequestManager.getInstance().send({ var self = this;
method: this.setter, RequestManager.getInstance().sendAsync({
params: [this.formatInput(value)] method: this.getter
}, function (err, result) {
if (err) {
return callback(err);
}
callback(err, self.formatOutput(result));
}); });
}; };
module.exports = Property; module.exports = Property;
},{"./requestmanager":25}],24:[function(require,module,exports){ },{"./requestmanager":24}],23:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -3261,7 +3413,7 @@ QtSyncProvider.prototype.send = function (payload) {
module.exports = QtSyncProvider; module.exports = QtSyncProvider;
},{}],25:[function(require,module,exports){ },{}],24:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -3369,6 +3521,33 @@ RequestManager.prototype.sendAsync = function (data, callback) {
}); });
}; };
/**
* Should be called to asynchronously send batch request
*
* @method sendBatch
* @param {Array} batch data
* @param {Function} callback
*/
RequestManager.prototype.sendBatch = function (data, callback) {
if (!this.provider) {
return callback(errors.InvalidProvider());
}
var payload = Jsonrpc.getInstance().toBatchPayload(data);
this.provider.sendAsync(payload, function (err, results) {
if (err) {
return callback(err);
}
if (!utils.isArray(results)) {
return callback(errors.InvalidResponse(results));
}
callback(err, results);
});
};
/** /**
* Should be used to set provider of request manager * Should be used to set provider of request manager
* *
@ -3482,7 +3661,7 @@ RequestManager.prototype.poll = function () {
module.exports = RequestManager; module.exports = RequestManager;
},{"../utils/config":7,"../utils/utils":8,"./errors":13,"./jsonrpc":20}],26:[function(require,module,exports){ },{"../utils/config":5,"../utils/utils":6,"./errors":12,"./jsonrpc":19}],25:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -3552,7 +3731,7 @@ module.exports = {
}; };
},{"./formatters":17,"./method":21}],27:[function(require,module,exports){ },{"./formatters":16,"./method":20}],26:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -3580,7 +3759,20 @@ var Method = require('./method');
/// @returns an array of objects describing web3.eth.filter api methods /// @returns an array of objects describing web3.eth.filter api methods
var eth = function () { var eth = function () {
var newFilterCall = function (args) { var newFilterCall = function (args) {
return typeof args[0] === 'string' ? 'eth_newBlockFilter' : 'eth_newFilter'; var type = args[0];
switch(type) {
case 'latest':
args.pop();
this.params = 0;
return 'eth_newBlockFilter';
case 'pending':
args.pop();
this.params = 0;
return 'eth_newPendingTransactionFilter';
default:
return 'eth_newFilter';
}
}; };
var newFilter = new Method({ var newFilter = new Method({
@ -3655,7 +3847,7 @@ module.exports = {
}; };
},{"./method":21}],28:[function(require,module,exports){ },{"./method":20}],27:[function(require,module,exports){
},{}],"bignumber.js":[function(require,module,exports){ },{}],"bignumber.js":[function(require,module,exports){
'use strict'; 'use strict';
@ -3668,7 +3860,6 @@ 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.abi = require('./lib/solidity/abi');
// dont override global variable // dont override global variable
if (typeof window !== 'undefined' && typeof window.web3 === 'undefined') { if (typeof window !== 'undefined' && typeof window.web3 === 'undefined') {
@ -3678,7 +3869,7 @@ if (typeof window !== 'undefined' && typeof window.web3 === 'undefined') {
module.exports = web3; module.exports = web3;
},{"./lib/solidity/abi":1,"./lib/web3":10,"./lib/web3/contract":11,"./lib/web3/httpprovider":19,"./lib/web3/qtsync":24}]},{},["web3"]) },{"./lib/web3":8,"./lib/web3/contract":10,"./lib/web3/httpprovider":18,"./lib/web3/qtsync":23}]},{},["web3"])
//# sourceMappingURL=web3-light.js.map //# sourceMappingURL=web3-light.js.map

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

File diff suppressed because one or more lines are too long

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

File diff suppressed because one or more lines are too long

593
libjsqrc/ethereumjs/dist/web3.js

@ -15,52 +15,6 @@ require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof requ
You should have received a copy of the GNU Lesser General Public License 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/>. along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
*/ */
/**
* @file abi.js
* @author Marek Kotewicz <marek@ethdev.com>
* @author Gav Wood <g@ethdev.com>
* @date 2014
*/
var coder = require('./coder');
var utils = require('./utils');
var formatConstructorParams = function (abi, params) {
var constructor = utils.getConstructor(abi, params.length);
if (!constructor) {
if (params.length > 0) {
console.warn("didn't found matching constructor, using default one");
}
return '';
}
return coder.encodeParams(constructor.inputs.map(function (input) {
return input.type;
}), params);
};
module.exports = {
formatConstructorParams: formatConstructorParams
};
},{"./coder":2,"./utils":5}],2:[function(require,module,exports){
/*
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 coder.js * @file coder.js
* @author Marek Kotewicz <marek@ethdev.com> * @author Marek Kotewicz <marek@ethdev.com>
@ -328,7 +282,7 @@ var coder = new SolidityCoder([
module.exports = coder; module.exports = coder;
},{"../utils/utils":8,"./formatters":3,"./param":4,"bignumber.js":"bignumber.js"}],3:[function(require,module,exports){ },{"../utils/utils":6,"./formatters":2,"./param":3,"bignumber.js":"bignumber.js"}],2:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -548,7 +502,7 @@ module.exports = {
}; };
},{"../utils/config":7,"../utils/utils":8,"./param":4,"bignumber.js":"bignumber.js"}],4:[function(require,module,exports){ },{"../utils/config":5,"../utils/utils":6,"./param":3,"bignumber.js":"bignumber.js"}],3:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -760,54 +714,7 @@ SolidityParam.decodeArray = function (bytes, index) {
module.exports = SolidityParam; module.exports = SolidityParam;
},{"../utils/utils":8}],5:[function(require,module,exports){ },{"../utils/utils":6}],4:[function(require,module,exports){
/*
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 utils.js
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
*/
/**
* Returns the contstructor with matching number of arguments
*
* @method getConstructor
* @param {Array} abi
* @param {Number} numberOfArgs
* @returns {Object} constructor function abi
*/
var getConstructor = function (abi, numberOfArgs) {
return abi.filter(function (f) {
return f.type === 'constructor' && f.inputs.length === numberOfArgs;
})[0];
};
//var getSupremeType = function (type) {
//return type.substr(0, type.indexOf('[')) + ']';
//};
module.exports = {
getConstructor: getConstructor
};
},{}],6:[function(require,module,exports){
'use strict'; 'use strict';
// go env doesn't have and need XMLHttpRequest // go env doesn't have and need XMLHttpRequest
@ -818,7 +725,7 @@ if (typeof XMLHttpRequest === 'undefined') {
} }
},{}],7:[function(require,module,exports){ },{}],5:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -890,7 +797,7 @@ module.exports = {
}; };
},{"bignumber.js":"bignumber.js"}],8:[function(require,module,exports){ },{"bignumber.js":"bignumber.js"}],6:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -1256,6 +1163,7 @@ var toAddress = function (address) {
return '0x' + padLeft(toHex(address).substr(2), 40); return '0x' + padLeft(toHex(address).substr(2), 40);
}; };
/** /**
* Returns true if object is BigNumber, otherwise false * Returns true if object is BigNumber, otherwise false
* *
@ -1366,12 +1274,12 @@ module.exports = {
}; };
},{"bignumber.js":"bignumber.js"}],9:[function(require,module,exports){ },{"bignumber.js":"bignumber.js"}],7:[function(require,module,exports){
module.exports={ module.exports={
"version": "0.3.6" "version": "0.4.2"
} }
},{}],10:[function(require,module,exports){ },{}],8:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -1411,6 +1319,7 @@ var RequestManager = require('./web3/requestmanager');
var c = require('./utils/config'); var c = require('./utils/config');
var Method = require('./web3/method'); var Method = require('./web3/method');
var Property = require('./web3/property'); var Property = require('./web3/property');
var Batch = require('./web3/batch');
var web3Methods = [ var web3Methods = [
new Method({ new Method({
@ -1503,6 +1412,9 @@ 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.createBatch = function () {
return new Batch();
};
// ADD defaultblock // ADD defaultblock
Object.defineProperty(web3.eth, 'defaultBlock', { Object.defineProperty(web3.eth, 'defaultBlock', {
@ -1538,7 +1450,70 @@ setupMethods(web3.shh, shh.methods);
module.exports = web3; module.exports = web3;
},{"./utils/config":7,"./utils/utils":8,"./version.json":9,"./web3/db":12,"./web3/eth":14,"./web3/filter":16,"./web3/formatters":17,"./web3/method":21,"./web3/net":22,"./web3/property":23,"./web3/requestmanager":25,"./web3/shh":26,"./web3/watches":27}],11:[function(require,module,exports){ },{"./utils/config":5,"./utils/utils":6,"./version.json":7,"./web3/batch":9,"./web3/db":11,"./web3/eth":13,"./web3/filter":15,"./web3/formatters":16,"./web3/method":20,"./web3/net":21,"./web3/property":22,"./web3/requestmanager":24,"./web3/shh":25,"./web3/watches":26}],9:[function(require,module,exports){
/*
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 batch.js
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
*/
var RequestManager = require('./requestmanager');
var Batch = function () {
this.requests = [];
};
/**
* Should be called to add create new request to batch request
*
* @method add
* @param {Object} jsonrpc requet object
*/
Batch.prototype.add = function (request) {
this.requests.push(request);
};
/**
* Should be called to execute batch request
*
* @method execute
*/
Batch.prototype.execute = function () {
var requests = this.requests;
RequestManager.getInstance().sendBatch(requests, function (err, results) {
results = results || [];
requests.map(function (request, index) {
return results[index] || {};
}).map(function (result, index) {
return requests[index].format ? requests[index].format(result.result) : result.result;
}).forEach(function (result, index) {
if (requests[index].callback) {
requests[index].callback(err, result);
}
});
});
};
module.exports = Batch;
},{"./requestmanager":24}],10:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -1562,13 +1537,39 @@ module.exports = web3;
*/ */
var web3 = require('../web3'); var web3 = require('../web3');
var solAbi = require('../solidity/abi');
var utils = require('../utils/utils'); var utils = require('../utils/utils');
var coder = require('../solidity/coder');
var SolidityEvent = require('./event'); var SolidityEvent = require('./event');
var SolidityFunction = require('./function'); var SolidityFunction = require('./function');
var addFunctionsToContract = function (contract, desc) { /**
desc.filter(function (json) { * Should be called to encode constructor params
*
* @method encodeConstructorParams
* @param {Array} abi
* @param {Array} constructor params
*/
var encodeConstructorParams = function (abi, params) {
return abi.filter(function (json) {
return json.type === 'constructor' && json.inputs.length === params.length;
}).map(function (json) {
return json.inputs.map(function (input) {
return input.type;
});
}).map(function (types) {
return coder.encodeParams(types, params);
})[0] || '';
};
/**
* Should be called to add functions to contract object
*
* @method addFunctionsToContract
* @param {Contract} contract
* @param {Array} abi
*/
var addFunctionsToContract = function (contract, abi) {
abi.filter(function (json) {
return json.type === 'function'; return json.type === 'function';
}).map(function (json) { }).map(function (json) {
return new SolidityFunction(json, contract.address); return new SolidityFunction(json, contract.address);
@ -1577,8 +1578,15 @@ var addFunctionsToContract = function (contract, desc) {
}); });
}; };
var addEventsToContract = function (contract, desc) { /**
desc.filter(function (json) { * Should be called to add events to contract object
*
* @method addEventsToContract
* @param {Contract} contract
* @param {Array} abi
*/
var addEventsToContract = function (contract, abi) {
abi.filter(function (json) {
return json.type === 'event'; return json.type === 'event';
}).map(function (json) { }).map(function (json) {
return new SolidityEvent(json, contract.address); return new SolidityEvent(json, contract.address);
@ -1588,65 +1596,106 @@ var addEventsToContract = function (contract, desc) {
}; };
/** /**
* This method should be called when we want to call / transact some solidity method from javascript * Should be called to create new ContractFactory
* it returns an object which has same methods available as solidity contract description
* usage example:
*
* var abi = [{
* name: 'myMethod',
* inputs: [{ name: 'a', type: 'string' }],
* outputs: [{name: 'd', type: 'string' }]
* }]; // contract abi
*
* var MyContract = web3.eth.contract(abi); // creation of contract prototype
* *
* var contractInstance = new MyContract('0x0123123121'); * @method contract
* * @param {Array} abi
* contractInstance.myMethod('this is test string param for call'); // myMethod call (implicit, default) * @returns {ContractFactory} new contract factory
* contractInstance.call().myMethod('this is test string param for call'); // myMethod call (explicit)
* contractInstance.sendTransaction().myMethod('this is test string param for transact'); // myMethod sendTransaction
*
* @param abi - abi json description of the contract, which is being created
* @returns contract object
*/ */
var contract = function (abi) { var contract = function (abi) {
return new ContractFactory(abi);
};
// return prototype /**
return Contract.bind(null, abi); * Should be called to create new ContractFactory instance
*
* @method ContractFactory
* @param {Array} abi
*/
var ContractFactory = function (abi) {
this.abi = abi;
}; };
var Contract = function (abi, options) { /**
* Should be called to create new contract on a blockchain
*
* @method new
* @param {Any} contract constructor param1 (optional)
* @param {Any} contract constructor param2 (optional)
* @param {Object} contract transaction object (required)
* @param {Function} callback
* @returns {Contract} returns contract if no callback was passed,
* otherwise calls callback function (err, contract)
*/
ContractFactory.prototype.new = function () {
// parse arguments
var options = {}; // required!
var callback;
this.address = ''; var args = Array.prototype.slice.call(arguments);
if (utils.isAddress(options)) { if (utils.isFunction(args[args.length - 1])) {
this.address = options; callback = args.pop();
} else { // is an object!
// TODO, parse the rest of the args
options = options || {};
var args = Array.prototype.slice.call(arguments, 2);
var bytes = solAbi.formatConstructorParams(abi, args);
options.data += bytes;
this.address = web3.eth.sendTransaction(options);
} }
addFunctionsToContract(this, abi); var last = args[args.length - 1];
addEventsToContract(this, abi); if (utils.isObject(last) && !utils.isArray(last)) {
options = args.pop();
}
// throw an error if there are no options
var bytes = encodeConstructorParams(this.abi, args);
options.data += bytes;
if (!callback) {
var address = web3.eth.sendTransaction(options);
return this.at(address);
}
var self = this;
web3.eth.sendTransaction(options, function (err, address) {
if (err) {
callback(err);
}
self.at(address, callback);
});
}; };
Contract.prototype.call = function () { /**
console.error('contract.call is deprecated'); * Should be called to get access to existing contract on a blockchain
return this; *
* @method at
* @param {Address} contract address (required)
* @param {Function} callback {optional)
* @returns {Contract} returns contract if no callback was passed,
* otherwise calls callback function (err, contract)
*/
ContractFactory.prototype.at = function (address, callback) {
// TODO: address is required
if (callback) {
callback(null, new Contract(this.abi, address));
}
return new Contract(this.abi, address);
}; };
Contract.prototype.sendTransaction = function () { /**
console.error('contract.sendTransact is deprecated'); * Should be called to create new contract instance
return this; *
* @method Contract
* @param {Array} abi
* @param {Address} contract address
*/
var Contract = function (abi, address) {
this.address = address;
addFunctionsToContract(this, abi);
addEventsToContract(this, abi);
}; };
module.exports = contract; module.exports = contract;
},{"../solidity/abi":1,"../utils/utils":8,"../web3":10,"./event":15,"./function":18}],12:[function(require,module,exports){ },{"../solidity/coder":1,"../utils/utils":6,"../web3":8,"./event":14,"./function":17}],11:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -1704,7 +1753,7 @@ module.exports = {
methods: methods methods: methods
}; };
},{"./method":21}],13:[function(require,module,exports){ },{"./method":20}],12:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -1744,7 +1793,7 @@ module.exports = {
}; };
},{}],14:[function(require,module,exports){ },{}],13:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -1921,6 +1970,14 @@ var call = new Method({
inputFormatter: [formatters.inputTransactionFormatter, formatters.inputDefaultBlockNumberFormatter] inputFormatter: [formatters.inputTransactionFormatter, formatters.inputDefaultBlockNumberFormatter]
}); });
var estimateGas = new Method({
name: 'estimateGas',
call: 'eth_estimateGas',
params: 1,
inputFormatter: [formatters.inputTransactionFormatter],
outputFormatter: utils.toDecimal
});
var compileSolidity = new Method({ var compileSolidity = new Method({
name: 'compile.solidity', name: 'compile.solidity',
call: 'eth_compileSolidity', call: 'eth_compileSolidity',
@ -1939,6 +1996,18 @@ var compileSerpent = new Method({
params: 1 params: 1
}); });
var submitWork = new Method({
name: 'submitWork',
call: 'eth_submitWork',
params: 3
});
var getWork = new Method({
name: 'getWork',
call: 'eth_getWork',
params: 0
});
var methods = [ var methods = [
getBalance, getBalance,
getStorageAt, getStorageAt,
@ -1952,10 +2021,13 @@ var methods = [
getTransactionFromBlock, getTransactionFromBlock,
getTransactionCount, getTransactionCount,
call, call,
estimateGas,
sendTransaction, sendTransaction,
compileSolidity, compileSolidity,
compileLLL, compileLLL,
compileSerpent, compileSerpent,
submitWork,
getWork
]; ];
/// @returns an array of objects describing web3.eth api properties /// @returns an array of objects describing web3.eth api properties
@ -1998,7 +2070,7 @@ module.exports = {
}; };
},{"../utils/utils":8,"./formatters":17,"./method":21,"./property":23}],15:[function(require,module,exports){ },{"../utils/utils":6,"./formatters":16,"./method":20,"./property":22}],14:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -2194,7 +2266,7 @@ SolidityEvent.prototype.attachToContract = function (contract) {
module.exports = SolidityEvent; module.exports = SolidityEvent;
},{"../solidity/coder":2,"../utils/utils":8,"../web3":10,"./formatters":17}],16:[function(require,module,exports){ },{"../solidity/coder":1,"../utils/utils":6,"../web3":8,"./formatters":16}],15:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -2351,7 +2423,7 @@ Filter.prototype.get = function (callback) {
module.exports = Filter; module.exports = Filter;
},{"../utils/utils":8,"./formatters":17,"./requestmanager":25}],17:[function(require,module,exports){ },{"../utils/utils":6,"./formatters":16,"./requestmanager":24}],16:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -2571,7 +2643,7 @@ module.exports = {
}; };
},{"../utils/config":7,"../utils/utils":8}],18:[function(require,module,exports){ },{"../utils/config":5,"../utils/utils":6}],17:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -2588,7 +2660,7 @@ module.exports = {
You should have received a copy of the GNU Lesser General Public License 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/>. along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** /**
* @file function.js * @file function.js
* @author Marek Kotewicz <marek@ethdev.com> * @author Marek Kotewicz <marek@ethdev.com>
* @date 2015 * @date 2015
@ -2613,18 +2685,23 @@ var SolidityFunction = function (json, address) {
this._address = address; this._address = address;
}; };
SolidityFunction.prototype.extractCallback = function (args) {
if (utils.isFunction(args[args.length - 1])) {
return args.pop(); // modify the args array!
}
};
/** /**
* Should be used to create payload from arguments * Should be used to create payload from arguments
* *
* @method toPayload * @method toPayload
* @param {...} solidity function params * @param {Array} solidity function params
* @param {Object} optional payload options * @param {Object} optional payload options
*/ */
SolidityFunction.prototype.toPayload = function () { SolidityFunction.prototype.toPayload = function (args) {
var args = Array.prototype.slice.call(arguments);
var options = {}; var options = {};
if (args.length > this._inputTypes.length && utils.isObject(args[args.length -1])) { if (args.length > this._inputTypes.length && utils.isObject(args[args.length -1])) {
options = args.pop(); options = args[args.length - 1];
} }
options.to = this._address; options.to = this._address;
options.data = '0x' + this.signature() + coder.encodeParams(this._inputTypes, args); options.data = '0x' + this.signature() + coder.encodeParams(this._inputTypes, args);
@ -2641,19 +2718,41 @@ SolidityFunction.prototype.signature = function () {
return web3.sha3(web3.fromAscii(this._name)).slice(2, 10); return web3.sha3(web3.fromAscii(this._name)).slice(2, 10);
}; };
SolidityFunction.prototype.unpackOutput = function (output) {
if (output === null) {
return;
}
output = output.length >= 2 ? output.slice(2) : output;
var result = coder.decodeParams(this._outputTypes, output);
return result.length === 1 ? result[0] : result;
};
/** /**
* Should be used to call function * Calls a contract function.
* *
* @method call * @method call
* @param {Object} options * @param {...Object} Contract function arguments
* @param {function} If the last argument is a function, the contract function
* call will be asynchronous, and the callback will be passed the
* error and result.
* @return {String} output bytes * @return {String} output bytes
*/ */
SolidityFunction.prototype.call = function () { SolidityFunction.prototype.call = function () {
var payload = this.toPayload.apply(this, Array.prototype.slice.call(arguments)); var args = Array.prototype.slice.call(arguments);
var output = web3.eth.call(payload); var callback = this.extractCallback(args);
output = output.length >= 2 ? output.slice(2) : output; var payload = this.toPayload(args);
var result = coder.decodeParams(this._outputTypes, output);
return result.length === 1 ? result[0] : result; if (!callback) {
var output = web3.eth.call(payload);
return this.unpackOutput(output);
}
var self = this;
web3.eth.call(payload, function (error, output) {
callback(error, self.unpackOutput(output));
});
}; };
/** /**
@ -2663,8 +2762,16 @@ SolidityFunction.prototype.call = function () {
* @param {Object} options * @param {Object} options
*/ */
SolidityFunction.prototype.sendTransaction = function () { SolidityFunction.prototype.sendTransaction = function () {
var payload = this.toPayload.apply(this, Array.prototype.slice.call(arguments)); var args = Array.prototype.slice.call(arguments);
web3.eth.sendTransaction(payload); var callback = this.extractCallback(args);
var payload = this.toPayload(args);
if (!callback) {
web3.eth.sendTransaction(payload);
return;
}
web3.eth.sendTransaction(payload, callback);
}; };
/** /**
@ -2679,7 +2786,7 @@ SolidityFunction.prototype.displayName = function () {
/** /**
* Should be used to get function type name * Should be used to get function type name
* *
* @method typeName * @method typeName
* @return {String} type name of the function * @return {String} type name of the function
*/ */
@ -2687,6 +2794,25 @@ SolidityFunction.prototype.typeName = function () {
return utils.extractTypeName(this._name); return utils.extractTypeName(this._name);
}; };
/**
* Should be called to get rpc requests from solidity function
*
* @method request
* @returns {Object}
*/
SolidityFunction.prototype.request = function () {
var args = Array.prototype.slice.call(arguments);
var callback = this.extractCallback(args);
var payload = this.toPayload(args);
var format = this.unpackOutput.bind(this);
return {
callback: callback,
payload: payload,
format: format
};
};
/** /**
* Should be called to execute function * Should be called to execute function
* *
@ -2694,7 +2820,7 @@ SolidityFunction.prototype.typeName = function () {
*/ */
SolidityFunction.prototype.execute = function () { SolidityFunction.prototype.execute = function () {
var transaction = !this._constant; var transaction = !this._constant;
// send transaction // send transaction
if (transaction) { if (transaction) {
return this.sendTransaction.apply(this, Array.prototype.slice.call(arguments)); return this.sendTransaction.apply(this, Array.prototype.slice.call(arguments));
@ -2712,6 +2838,7 @@ SolidityFunction.prototype.execute = function () {
*/ */
SolidityFunction.prototype.attachToContract = function (contract) { SolidityFunction.prototype.attachToContract = function (contract) {
var execute = this.execute.bind(this); var execute = this.execute.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);
var displayName = this.displayName(); var displayName = this.displayName();
@ -2724,7 +2851,7 @@ SolidityFunction.prototype.attachToContract = function (contract) {
module.exports = SolidityFunction; module.exports = SolidityFunction;
},{"../solidity/coder":2,"../utils/utils":8,"../web3":10}],19:[function(require,module,exports){ },{"../solidity/coder":1,"../utils/utils":6,"../web3":8}],18:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -2816,7 +2943,7 @@ HttpProvider.prototype.sendAsync = function (payload, callback) {
module.exports = HttpProvider; module.exports = HttpProvider;
},{"./errors":13,"xmlhttprequest":6}],20:[function(require,module,exports){ },{"./errors":12,"xmlhttprequest":4}],19:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -2909,7 +3036,7 @@ Jsonrpc.prototype.toBatchPayload = function (messages) {
module.exports = Jsonrpc; module.exports = Jsonrpc;
},{}],21:[function(require,module,exports){ },{}],20:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -2966,7 +3093,6 @@ Method.prototype.extractCallback = function (args) {
if (utils.isFunction(args[args.length - 1])) { if (utils.isFunction(args[args.length - 1])) {
return args.pop(); // modify the args array! return args.pop(); // modify the args array!
} }
return null;
}; };
/** /**
@ -3019,6 +3145,7 @@ Method.prototype.formatOutput = function (result) {
*/ */
Method.prototype.attachToObject = function (obj) { Method.prototype.attachToObject = function (obj) {
var func = this.send.bind(this); var func = this.send.bind(this);
func.request = this.request.bind(this);
func.call = this.call; // that's ugly. filter.js uses it func.call = this.call; // that's ugly. filter.js uses it
var name = this.name.split('.'); var name = this.name.split('.');
if (name.length > 1) { if (name.length > 1) {
@ -3049,6 +3176,19 @@ Method.prototype.toPayload = function (args) {
}; };
}; };
/**
* Should be called to create pure JSONRPC request which can be used in batch request
*
* @method request
* @param {...} params
* @return {Object} jsonrpc request
*/
Method.prototype.request = function () {
var payload = this.toPayload(Array.prototype.slice.call(arguments));
payload.format = this.formatOutput.bind(this);
return payload;
};
/** /**
* Should send request to the API * Should send request to the API
* *
@ -3061,7 +3201,7 @@ Method.prototype.send = function () {
if (payload.callback) { if (payload.callback) {
var self = this; var self = this;
return RequestManager.getInstance().sendAsync(payload, function (err, result) { return RequestManager.getInstance().sendAsync(payload, function (err, result) {
payload.callback(null, self.formatOutput(result)); payload.callback(err, self.formatOutput(result));
}); });
} }
return this.formatOutput(RequestManager.getInstance().send(payload)); return this.formatOutput(RequestManager.getInstance().send(payload));
@ -3070,7 +3210,7 @@ Method.prototype.send = function () {
module.exports = Method; module.exports = Method;
},{"../utils/utils":8,"./errors":13,"./requestmanager":25}],22:[function(require,module,exports){ },{"../utils/utils":6,"./errors":12,"./requestmanager":24}],21:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -3120,7 +3260,7 @@ module.exports = {
}; };
},{"../utils/utils":8,"./property":23}],23:[function(require,module,exports){ },{"../utils/utils":6,"./property":22}],22:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -3186,16 +3326,23 @@ Property.prototype.formatOutput = function (result) {
Property.prototype.attachToObject = function (obj) { Property.prototype.attachToObject = function (obj) {
var proto = { var proto = {
get: this.get.bind(this), get: this.get.bind(this),
set: this.set.bind(this)
}; };
var name = this.name.split('.'); var names = this.name.split('.');
if (name.length > 1) { var name = names[0];
obj[name[0]] = obj[name[0]] || {}; if (names.length > 1) {
Object.defineProperty(obj[name[0]], name[1], proto); obj[names[0]] = obj[names[0]] || {};
} else { obj = obj[names[0]];
Object.defineProperty(obj, name[0], proto); name = names[1];
} }
Object.defineProperty(obj, name, proto);
var toAsyncName = function (prefix, name) {
return prefix + name.charAt(0).toUpperCase() + name.slice(1);
};
obj[toAsyncName('get', name)] = this.getAsync.bind(this);
}; };
/** /**
@ -3211,22 +3358,27 @@ Property.prototype.get = function () {
}; };
/** /**
* Should be used to set value of the property * Should be used to asynchrounously get value of property
* *
* @method set * @method getAsync
* @param {Object} new value of the property * @param {Function}
*/ */
Property.prototype.set = function (value) { Property.prototype.getAsync = function (callback) {
return RequestManager.getInstance().send({ var self = this;
method: this.setter, RequestManager.getInstance().sendAsync({
params: [this.formatInput(value)] method: this.getter
}, function (err, result) {
if (err) {
return callback(err);
}
callback(err, self.formatOutput(result));
}); });
}; };
module.exports = Property; module.exports = Property;
},{"./requestmanager":25}],24:[function(require,module,exports){ },{"./requestmanager":24}],23:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -3261,7 +3413,7 @@ QtSyncProvider.prototype.send = function (payload) {
module.exports = QtSyncProvider; module.exports = QtSyncProvider;
},{}],25:[function(require,module,exports){ },{}],24:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -3369,6 +3521,33 @@ RequestManager.prototype.sendAsync = function (data, callback) {
}); });
}; };
/**
* Should be called to asynchronously send batch request
*
* @method sendBatch
* @param {Array} batch data
* @param {Function} callback
*/
RequestManager.prototype.sendBatch = function (data, callback) {
if (!this.provider) {
return callback(errors.InvalidProvider());
}
var payload = Jsonrpc.getInstance().toBatchPayload(data);
this.provider.sendAsync(payload, function (err, results) {
if (err) {
return callback(err);
}
if (!utils.isArray(results)) {
return callback(errors.InvalidResponse(results));
}
callback(err, results);
});
};
/** /**
* Should be used to set provider of request manager * Should be used to set provider of request manager
* *
@ -3482,7 +3661,7 @@ RequestManager.prototype.poll = function () {
module.exports = RequestManager; module.exports = RequestManager;
},{"../utils/config":7,"../utils/utils":8,"./errors":13,"./jsonrpc":20}],26:[function(require,module,exports){ },{"../utils/config":5,"../utils/utils":6,"./errors":12,"./jsonrpc":19}],25:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -3552,7 +3731,7 @@ module.exports = {
}; };
},{"./formatters":17,"./method":21}],27:[function(require,module,exports){ },{"./formatters":16,"./method":20}],26:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -3580,7 +3759,20 @@ var Method = require('./method');
/// @returns an array of objects describing web3.eth.filter api methods /// @returns an array of objects describing web3.eth.filter api methods
var eth = function () { var eth = function () {
var newFilterCall = function (args) { var newFilterCall = function (args) {
return typeof args[0] === 'string' ? 'eth_newBlockFilter' : 'eth_newFilter'; var type = args[0];
switch(type) {
case 'latest':
args.pop();
this.params = 0;
return 'eth_newBlockFilter';
case 'pending':
args.pop();
this.params = 0;
return 'eth_newPendingTransactionFilter';
default:
return 'eth_newFilter';
}
}; };
var newFilter = new Method({ var newFilter = new Method({
@ -3655,7 +3847,7 @@ module.exports = {
}; };
},{"./method":21}],28:[function(require,module,exports){ },{"./method":20}],27:[function(require,module,exports){
},{}],"bignumber.js":[function(require,module,exports){ },{}],"bignumber.js":[function(require,module,exports){
/*! bignumber.js v2.0.7 https://github.com/MikeMcl/bignumber.js/LICENCE */ /*! bignumber.js v2.0.7 https://github.com/MikeMcl/bignumber.js/LICENCE */
@ -6342,12 +6534,11 @@ module.exports = {
} }
})(this); })(this);
},{"crypto":28}],"web3":[function(require,module,exports){ },{"crypto":27}],"web3":[function(require,module,exports){
var web3 = require('./lib/web3'); 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.abi = require('./lib/solidity/abi');
// dont override global variable // dont override global variable
if (typeof window !== 'undefined' && typeof window.web3 === 'undefined') { if (typeof window !== 'undefined' && typeof window.web3 === 'undefined') {
@ -6357,7 +6548,7 @@ if (typeof window !== 'undefined' && typeof window.web3 === 'undefined') {
module.exports = web3; module.exports = web3;
},{"./lib/solidity/abi":1,"./lib/web3":10,"./lib/web3/contract":11,"./lib/web3/httpprovider":19,"./lib/web3/qtsync":24}]},{},["web3"]) },{"./lib/web3":8,"./lib/web3/contract":10,"./lib/web3/httpprovider":18,"./lib/web3/qtsync":23}]},{},["web3"])
//# sourceMappingURL=web3.js.map //# sourceMappingURL=web3.js.map

30
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

44
libjsqrc/ethereumjs/example/contract.html

@ -8,14 +8,16 @@
var web3 = require('web3'); var web3 = require('web3');
web3.setProvider(new web3.providers.HttpProvider("http://localhost:8545")); web3.setProvider(new web3.providers.HttpProvider("http://localhost:8545"));
// solidity source code // solidity code code
/*var source = "" +*/ var source = "" +
/*"contract test {\n" +*/ "contract test {\n" +
/*" function multiply(uint a) constant returns(uint d) {\n" +*/ " function multiply(uint a) constant returns(uint d) {\n" +
/*" return a * 7;\n" +*/ " return a * 7;\n" +
/*" }\n" +*/ " }\n" +
/*"}\n";*/ "}\n";
var source = "605280600c6000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063c6888fa114602e57005b60376004356041565b8060005260206000f35b6000600782029050604d565b91905056";
var code = web3.eth.compile.solidity(source).code;
/*var code = "605280600c6000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063c6888fa114602e57005b60376004356041565b8060005260206000f35b6000600782029050604d565b91905056";*/
// contract description, this is autogenerated using solc CLI // contract description, this is autogenerated using solc CLI
var desc = [{ var desc = [{
@ -37,15 +39,30 @@
function createExampleContract() { function createExampleContract() {
// hide create button // hide create button
document.getElementById('create').style.visibility = 'hidden'; document.getElementById('create').style.visibility = 'hidden';
document.getElementById('source').innerText = source; document.getElementById('code').innerText = code;
// let's assume that coinbase is our account // let's assume that coinbase is our account
web3.eth.defaultAccount = web3.eth.coinbase; web3.eth.defaultAccount = web3.eth.coinbase;
var watch = web3.eth.filter('latest');
// create contract // create contract
var Contract = web3.eth.contract(desc); myContract = web3.eth.contract(desc).new({data: code});
myContract = new Contract({data: source}); console.log('address: ' + myContract.address);
document.getElementById('call').style.visibility = 'visible'; document.getElementById('status').innerText = "transaction sent, waiting for confirmation";
watch.watch(function (err, hash) {
var block = web3.eth.getBlock(hash, true);
var contractMined = block.transactions.reduce(function (mined, th) {
// TODO: compiled code do not have 0x prefix
return mined || (th.from === web3.eth.defaultAccount && th.input.indexOf(code) !== -1);
}, false);
if (contractMined) {
document.getElementById('status').innerText = 'Mined!';
document.getElementById('call').style.visibility = 'visible';
}
});
} }
function callExampleContract() { function callExampleContract() {
@ -61,7 +78,8 @@
</head> </head>
<body> <body>
<h1>contract</h1> <h1>contract</h1>
<div id="source"></div> <div id="code"></div>
<div id="status"></div>
<div id='create'> <div id='create'>
<button type="button" onClick="createExampleContract();">create example contract</button> <button type="button" onClick="createExampleContract();">create example contract</button>
</div> </div>

64
libjsqrc/ethereumjs/example/event_inc.html

@ -6,19 +6,20 @@
var web3 = require('web3'); var web3 = require('web3');
web3.setProvider(new web3.providers.HttpProvider('http://localhost:8545')); web3.setProvider(new web3.providers.HttpProvider('http://localhost:8545'));
/*var source = "" + */ var source = "" +
/*"contract Contract { " +*/ "contract Contract { " +
/*" event Incremented(bool indexed odd, uint x); " +*/ " event Incremented(bool indexed odd, uint x); " +
/*" function Contract() { " +*/ " function Contract() { " +
/*" x = 69; " +*/ " x = 70; " +
/*" } " +*/ " } " +
/*" function inc() { " +*/ " function inc() { " +
/*" ++x; " +*/ " ++x; " +
/*" Incremented(x % 2 == 1, x); " +*/ " Incremented(x % 2 == 1, x); " +
/*" } " +*/ " } " +
/*" uint x; " +*/ " uint x; " +
/*"}";*/ "}";
var source = "5b60456000600050819055505b608c8060196000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063371303c014602e57005b6034603a565b60006000f35b6000600081815054600101919050819055506001600260006000505406147f6e61ef44ac2747ff8b84d353a908eb8bd5c3fb118334d57698c5cfc7041196ad600060006000505481526020016000a25b56"; var code = web3.eth.compile.solidity(source).code;
/*var code = "5b60456000600050819055505b608c8060196000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063371303c014602e57005b6034603a565b60006000f35b6000600081815054600101919050819055506001600260006000505406147f6e61ef44ac2747ff8b84d353a908eb8bd5c3fb118334d57698c5cfc7041196ad600060006000505481526020016000a25b56";*/
var desc = [{ var desc = [{
"constant" : false, "constant" : false,
@ -51,13 +52,40 @@
var createContract = function () { var createContract = function () {
// let's assume that we have a private key to coinbase ;) // let's assume that we have a private key to coinbase ;)
web3.eth.defaultAccount = web3.eth.coinbase; web3.eth.defaultAccount = web3.eth.coinbase;
var Contract = web3.eth.contract(desc);
contract = new Contract({data: source}); var watch = web3.eth.filter('latest');
contract = web3.eth.contract(desc).new({data: code});
console.log('address: ' + contract.address);
document.getElementById('create').style.visibility = 'hidden';
document.getElementById('status').innerText = "transaction sent, waiting for confirmation";
watch.watch(function (err, hash) {
var block = web3.eth.getBlock(hash, true);
var contractMined = block.transactions.reduce(function (mined, th) {
// TODO: compiled code do not have 0x prefix
return mined || (th.from === web3.eth.defaultAccount && th.input.indexOf(code) !== -1);
}, false);
if (contractMined) {
document.getElementById('status').innerText = 'Mined!';
document.getElementById('call').style.visibility = 'visible';
}
});
contract.Incremented({odd: true}).watch(update); contract.Incremented({odd: true}).watch(update);
}; };
var counter = 0;
var callContract = function () { var callContract = function () {
counter++;
var all = 70 + counter;
document.getElementById('count').innerText = 'Transaction sent ' + counter + ' times. ' +
'Expected x value is: ' + (all - (all % 2 ? 0 : 1)) + ' ' +
'Waiting for the blocks to be mined...';
contract.inc(); contract.inc();
}; };
@ -66,12 +94,14 @@
</head> </head>
<body> <body>
<div id="status"></div>
<div> <div>
<button type="button" onClick="createContract();">create contract</button> <button id="create" type="button" onClick="createContract();">create contract</button>
</div> </div>
<div> <div>
<button type="button" onClick="callContract();">test1</button> <button id="call" style="visibility: hidden;" type="button" onClick="callContract();">test1</button>
</div> </div>
<div id='count'></div>
<div id="result"> <div id="result">
</div> </div>
</body> </body>

4
libjsqrc/ethereumjs/example/node-app.js

@ -2,11 +2,11 @@
var web3 = require("../index.js"); var web3 = require("../index.js");
web3.setProvider(new web3.providers.HttpProvider('http://localhost:8080')); web3.setProvider(new web3.providers.HttpProvider('http://localhost:8545'));
var coinbase = web3.eth.coinbase; var coinbase = web3.eth.coinbase;
console.log(coinbase); console.log(coinbase);
var balance = web3.eth.getBalance(coinbase); var balance = web3.eth.getBalance(coinbase);
console.log(balance); console.log(balance.toString(10));

1
libjsqrc/ethereumjs/index.js

@ -2,7 +2,6 @@ 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.abi = require('./lib/solidity/abi');
// dont override global variable // dont override global variable
if (typeof window !== 'undefined' && typeof window.web3 === 'undefined') { if (typeof window !== 'undefined' && typeof window.web3 === 'undefined') {

44
libjsqrc/ethereumjs/lib/solidity/abi.js

@ -1,44 +0,0 @@
/*
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 abi.js
* @author Marek Kotewicz <marek@ethdev.com>
* @author Gav Wood <g@ethdev.com>
* @date 2014
*/
var coder = require('./coder');
var utils = require('./utils');
var formatConstructorParams = function (abi, params) {
var constructor = utils.getConstructor(abi, params.length);
if (!constructor) {
if (params.length > 0) {
console.warn("didn't found matching constructor, using default one");
}
return '';
}
return coder.encodeParams(constructor.inputs.map(function (input) {
return input.type;
}), params);
};
module.exports = {
formatConstructorParams: formatConstructorParams
};

45
libjsqrc/ethereumjs/lib/solidity/utils.js

@ -1,45 +0,0 @@
/*
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 utils.js
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
*/
/**
* Returns the contstructor with matching number of arguments
*
* @method getConstructor
* @param {Array} abi
* @param {Number} numberOfArgs
* @returns {Object} constructor function abi
*/
var getConstructor = function (abi, numberOfArgs) {
return abi.filter(function (f) {
return f.type === 'constructor' && f.inputs.length === numberOfArgs;
})[0];
};
//var getSupremeType = function (type) {
//return type.substr(0, type.indexOf('[')) + ']';
//};
module.exports = {
getConstructor: getConstructor
};

1
libjsqrc/ethereumjs/lib/utils/utils.js

@ -363,6 +363,7 @@ var toAddress = function (address) {
return '0x' + padLeft(toHex(address).substr(2), 40); return '0x' + padLeft(toHex(address).substr(2), 40);
}; };
/** /**
* Returns true if object is BigNumber, otherwise false * Returns true if object is BigNumber, otherwise false
* *

2
libjsqrc/ethereumjs/lib/version.json

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

4
libjsqrc/ethereumjs/lib/web3.js

@ -37,6 +37,7 @@ var RequestManager = require('./web3/requestmanager');
var c = require('./utils/config'); var c = require('./utils/config');
var Method = require('./web3/method'); var Method = require('./web3/method');
var Property = require('./web3/property'); var Property = require('./web3/property');
var Batch = require('./web3/batch');
var web3Methods = [ var web3Methods = [
new Method({ new Method({
@ -129,6 +130,9 @@ 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.createBatch = function () {
return new Batch();
};
// ADD defaultblock // ADD defaultblock
Object.defineProperty(web3.eth, 'defaultBlock', { Object.defineProperty(web3.eth, 'defaultBlock', {

61
libjsqrc/ethereumjs/lib/web3/batch.js

@ -0,0 +1,61 @@
/*
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 batch.js
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
*/
var RequestManager = require('./requestmanager');
var Batch = function () {
this.requests = [];
};
/**
* Should be called to add create new request to batch request
*
* @method add
* @param {Object} jsonrpc requet object
*/
Batch.prototype.add = function (request) {
this.requests.push(request);
};
/**
* Should be called to execute batch request
*
* @method execute
*/
Batch.prototype.execute = function () {
var requests = this.requests;
RequestManager.getInstance().sendBatch(requests, function (err, results) {
results = results || [];
requests.map(function (request, index) {
return results[index] || {};
}).map(function (result, index) {
return requests[index].format ? requests[index].format(result.result) : result.result;
}).forEach(function (result, index) {
if (requests[index].callback) {
requests[index].callback(err, result);
}
});
});
};
module.exports = Batch;

166
libjsqrc/ethereumjs/lib/web3/contract.js

@ -21,13 +21,39 @@
*/ */
var web3 = require('../web3'); var web3 = require('../web3');
var solAbi = require('../solidity/abi');
var utils = require('../utils/utils'); var utils = require('../utils/utils');
var coder = require('../solidity/coder');
var SolidityEvent = require('./event'); var SolidityEvent = require('./event');
var SolidityFunction = require('./function'); var SolidityFunction = require('./function');
var addFunctionsToContract = function (contract, desc) { /**
desc.filter(function (json) { * Should be called to encode constructor params
*
* @method encodeConstructorParams
* @param {Array} abi
* @param {Array} constructor params
*/
var encodeConstructorParams = function (abi, params) {
return abi.filter(function (json) {
return json.type === 'constructor' && json.inputs.length === params.length;
}).map(function (json) {
return json.inputs.map(function (input) {
return input.type;
});
}).map(function (types) {
return coder.encodeParams(types, params);
})[0] || '';
};
/**
* Should be called to add functions to contract object
*
* @method addFunctionsToContract
* @param {Contract} contract
* @param {Array} abi
*/
var addFunctionsToContract = function (contract, abi) {
abi.filter(function (json) {
return json.type === 'function'; return json.type === 'function';
}).map(function (json) { }).map(function (json) {
return new SolidityFunction(json, contract.address); return new SolidityFunction(json, contract.address);
@ -36,8 +62,15 @@ var addFunctionsToContract = function (contract, desc) {
}); });
}; };
var addEventsToContract = function (contract, desc) { /**
desc.filter(function (json) { * Should be called to add events to contract object
*
* @method addEventsToContract
* @param {Contract} contract
* @param {Array} abi
*/
var addEventsToContract = function (contract, abi) {
abi.filter(function (json) {
return json.type === 'event'; return json.type === 'event';
}).map(function (json) { }).map(function (json) {
return new SolidityEvent(json, contract.address); return new SolidityEvent(json, contract.address);
@ -47,59 +80,100 @@ var addEventsToContract = function (contract, desc) {
}; };
/** /**
* This method should be called when we want to call / transact some solidity method from javascript * Should be called to create new ContractFactory
* it returns an object which has same methods available as solidity contract description
* usage example:
*
* var abi = [{
* name: 'myMethod',
* inputs: [{ name: 'a', type: 'string' }],
* outputs: [{name: 'd', type: 'string' }]
* }]; // contract abi
*
* var MyContract = web3.eth.contract(abi); // creation of contract prototype
*
* var contractInstance = new MyContract('0x0123123121');
* *
* contractInstance.myMethod('this is test string param for call'); // myMethod call (implicit, default) * @method contract
* contractInstance.call().myMethod('this is test string param for call'); // myMethod call (explicit) * @param {Array} abi
* contractInstance.sendTransaction().myMethod('this is test string param for transact'); // myMethod sendTransaction * @returns {ContractFactory} new contract factory
*
* @param abi - abi json description of the contract, which is being created
* @returns contract object
*/ */
var contract = function (abi) { var contract = function (abi) {
return new ContractFactory(abi);
};
// return prototype /**
return Contract.bind(null, abi); * Should be called to create new ContractFactory instance
*
* @method ContractFactory
* @param {Array} abi
*/
var ContractFactory = function (abi) {
this.abi = abi;
}; };
var Contract = function (abi, options) { /**
* Should be called to create new contract on a blockchain
this.address = ''; *
if (utils.isAddress(options)) { * @method new
this.address = options; * @param {Any} contract constructor param1 (optional)
} else { // is an object! * @param {Any} contract constructor param2 (optional)
// TODO, parse the rest of the args * @param {Object} contract transaction object (required)
options = options || {}; * @param {Function} callback
var args = Array.prototype.slice.call(arguments, 2); * @returns {Contract} returns contract if no callback was passed,
var bytes = solAbi.formatConstructorParams(abi, args); * otherwise calls callback function (err, contract)
options.data += bytes; */
this.address = web3.eth.sendTransaction(options); ContractFactory.prototype.new = function () {
// parse arguments
var options = {}; // required!
var callback;
var args = Array.prototype.slice.call(arguments);
if (utils.isFunction(args[args.length - 1])) {
callback = args.pop();
} }
addFunctionsToContract(this, abi); var last = args[args.length - 1];
addEventsToContract(this, abi); if (utils.isObject(last) && !utils.isArray(last)) {
options = args.pop();
}
// throw an error if there are no options
var bytes = encodeConstructorParams(this.abi, args);
options.data += bytes;
if (!callback) {
var address = web3.eth.sendTransaction(options);
return this.at(address);
}
var self = this;
web3.eth.sendTransaction(options, function (err, address) {
if (err) {
callback(err);
}
self.at(address, callback);
});
}; };
Contract.prototype.call = function () { /**
console.error('contract.call is deprecated'); * Should be called to get access to existing contract on a blockchain
return this; *
* @method at
* @param {Address} contract address (required)
* @param {Function} callback {optional)
* @returns {Contract} returns contract if no callback was passed,
* otherwise calls callback function (err, contract)
*/
ContractFactory.prototype.at = function (address, callback) {
// TODO: address is required
if (callback) {
callback(null, new Contract(this.abi, address));
}
return new Contract(this.abi, address);
}; };
Contract.prototype.sendTransaction = function () { /**
console.error('contract.sendTransact is deprecated'); * Should be called to create new contract instance
return this; *
* @method Contract
* @param {Array} abi
* @param {Address} contract address
*/
var Contract = function (abi, address) {
this.address = address;
addFunctionsToContract(this, abi);
addEventsToContract(this, abi);
}; };
module.exports = contract; module.exports = contract;

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

@ -174,6 +174,14 @@ var call = new Method({
inputFormatter: [formatters.inputTransactionFormatter, formatters.inputDefaultBlockNumberFormatter] inputFormatter: [formatters.inputTransactionFormatter, formatters.inputDefaultBlockNumberFormatter]
}); });
var estimateGas = new Method({
name: 'estimateGas',
call: 'eth_estimateGas',
params: 1,
inputFormatter: [formatters.inputTransactionFormatter],
outputFormatter: utils.toDecimal
});
var compileSolidity = new Method({ var compileSolidity = new Method({
name: 'compile.solidity', name: 'compile.solidity',
call: 'eth_compileSolidity', call: 'eth_compileSolidity',
@ -192,6 +200,18 @@ var compileSerpent = new Method({
params: 1 params: 1
}); });
var submitWork = new Method({
name: 'submitWork',
call: 'eth_submitWork',
params: 3
});
var getWork = new Method({
name: 'getWork',
call: 'eth_getWork',
params: 0
});
var methods = [ var methods = [
getBalance, getBalance,
getStorageAt, getStorageAt,
@ -205,10 +225,13 @@ var methods = [
getTransactionFromBlock, getTransactionFromBlock,
getTransactionCount, getTransactionCount,
call, call,
estimateGas,
sendTransaction, sendTransaction,
compileSolidity, compileSolidity,
compileLLL, compileLLL,
compileSerpent, compileSerpent,
submitWork,
getWork
]; ];
/// @returns an array of objects describing web3.eth api properties /// @returns an array of objects describing web3.eth api properties

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

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License 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/>. along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** /**
* @file function.js * @file function.js
* @author Marek Kotewicz <marek@ethdev.com> * @author Marek Kotewicz <marek@ethdev.com>
* @date 2015 * @date 2015
@ -39,18 +39,23 @@ var SolidityFunction = function (json, address) {
this._address = address; this._address = address;
}; };
SolidityFunction.prototype.extractCallback = function (args) {
if (utils.isFunction(args[args.length - 1])) {
return args.pop(); // modify the args array!
}
};
/** /**
* Should be used to create payload from arguments * Should be used to create payload from arguments
* *
* @method toPayload * @method toPayload
* @param {...} solidity function params * @param {Array} solidity function params
* @param {Object} optional payload options * @param {Object} optional payload options
*/ */
SolidityFunction.prototype.toPayload = function () { SolidityFunction.prototype.toPayload = function (args) {
var args = Array.prototype.slice.call(arguments);
var options = {}; var options = {};
if (args.length > this._inputTypes.length && utils.isObject(args[args.length -1])) { if (args.length > this._inputTypes.length && utils.isObject(args[args.length -1])) {
options = args.pop(); options = args[args.length - 1];
} }
options.to = this._address; options.to = this._address;
options.data = '0x' + this.signature() + coder.encodeParams(this._inputTypes, args); options.data = '0x' + this.signature() + coder.encodeParams(this._inputTypes, args);
@ -67,19 +72,41 @@ SolidityFunction.prototype.signature = function () {
return web3.sha3(web3.fromAscii(this._name)).slice(2, 10); return web3.sha3(web3.fromAscii(this._name)).slice(2, 10);
}; };
SolidityFunction.prototype.unpackOutput = function (output) {
if (output === null) {
return;
}
output = output.length >= 2 ? output.slice(2) : output;
var result = coder.decodeParams(this._outputTypes, output);
return result.length === 1 ? result[0] : result;
};
/** /**
* Should be used to call function * Calls a contract function.
* *
* @method call * @method call
* @param {Object} options * @param {...Object} Contract function arguments
* @param {function} If the last argument is a function, the contract function
* call will be asynchronous, and the callback will be passed the
* error and result.
* @return {String} output bytes * @return {String} output bytes
*/ */
SolidityFunction.prototype.call = function () { SolidityFunction.prototype.call = function () {
var payload = this.toPayload.apply(this, Array.prototype.slice.call(arguments)); var args = Array.prototype.slice.call(arguments);
var output = web3.eth.call(payload); var callback = this.extractCallback(args);
output = output.length >= 2 ? output.slice(2) : output; var payload = this.toPayload(args);
var result = coder.decodeParams(this._outputTypes, output);
return result.length === 1 ? result[0] : result; if (!callback) {
var output = web3.eth.call(payload);
return this.unpackOutput(output);
}
var self = this;
web3.eth.call(payload, function (error, output) {
callback(error, self.unpackOutput(output));
});
}; };
/** /**
@ -89,8 +116,16 @@ SolidityFunction.prototype.call = function () {
* @param {Object} options * @param {Object} options
*/ */
SolidityFunction.prototype.sendTransaction = function () { SolidityFunction.prototype.sendTransaction = function () {
var payload = this.toPayload.apply(this, Array.prototype.slice.call(arguments)); var args = Array.prototype.slice.call(arguments);
web3.eth.sendTransaction(payload); var callback = this.extractCallback(args);
var payload = this.toPayload(args);
if (!callback) {
web3.eth.sendTransaction(payload);
return;
}
web3.eth.sendTransaction(payload, callback);
}; };
/** /**
@ -105,7 +140,7 @@ SolidityFunction.prototype.displayName = function () {
/** /**
* Should be used to get function type name * Should be used to get function type name
* *
* @method typeName * @method typeName
* @return {String} type name of the function * @return {String} type name of the function
*/ */
@ -113,6 +148,25 @@ SolidityFunction.prototype.typeName = function () {
return utils.extractTypeName(this._name); return utils.extractTypeName(this._name);
}; };
/**
* Should be called to get rpc requests from solidity function
*
* @method request
* @returns {Object}
*/
SolidityFunction.prototype.request = function () {
var args = Array.prototype.slice.call(arguments);
var callback = this.extractCallback(args);
var payload = this.toPayload(args);
var format = this.unpackOutput.bind(this);
return {
callback: callback,
payload: payload,
format: format
};
};
/** /**
* Should be called to execute function * Should be called to execute function
* *
@ -120,7 +174,7 @@ SolidityFunction.prototype.typeName = function () {
*/ */
SolidityFunction.prototype.execute = function () { SolidityFunction.prototype.execute = function () {
var transaction = !this._constant; var transaction = !this._constant;
// send transaction // send transaction
if (transaction) { if (transaction) {
return this.sendTransaction.apply(this, Array.prototype.slice.call(arguments)); return this.sendTransaction.apply(this, Array.prototype.slice.call(arguments));
@ -138,6 +192,7 @@ SolidityFunction.prototype.execute = function () {
*/ */
SolidityFunction.prototype.attachToContract = function (contract) { SolidityFunction.prototype.attachToContract = function (contract) {
var execute = this.execute.bind(this); var execute = this.execute.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);
var displayName = this.displayName(); var displayName = this.displayName();

17
libjsqrc/ethereumjs/lib/web3/method.js

@ -54,7 +54,6 @@ Method.prototype.extractCallback = function (args) {
if (utils.isFunction(args[args.length - 1])) { if (utils.isFunction(args[args.length - 1])) {
return args.pop(); // modify the args array! return args.pop(); // modify the args array!
} }
return null;
}; };
/** /**
@ -107,6 +106,7 @@ Method.prototype.formatOutput = function (result) {
*/ */
Method.prototype.attachToObject = function (obj) { Method.prototype.attachToObject = function (obj) {
var func = this.send.bind(this); var func = this.send.bind(this);
func.request = this.request.bind(this);
func.call = this.call; // that's ugly. filter.js uses it func.call = this.call; // that's ugly. filter.js uses it
var name = this.name.split('.'); var name = this.name.split('.');
if (name.length > 1) { if (name.length > 1) {
@ -137,6 +137,19 @@ Method.prototype.toPayload = function (args) {
}; };
}; };
/**
* Should be called to create pure JSONRPC request which can be used in batch request
*
* @method request
* @param {...} params
* @return {Object} jsonrpc request
*/
Method.prototype.request = function () {
var payload = this.toPayload(Array.prototype.slice.call(arguments));
payload.format = this.formatOutput.bind(this);
return payload;
};
/** /**
* Should send request to the API * Should send request to the API
* *
@ -149,7 +162,7 @@ Method.prototype.send = function () {
if (payload.callback) { if (payload.callback) {
var self = this; var self = this;
return RequestManager.getInstance().sendAsync(payload, function (err, result) { return RequestManager.getInstance().sendAsync(payload, function (err, result) {
payload.callback(null, self.formatOutput(result)); payload.callback(err, self.formatOutput(result));
}); });
} }
return this.formatOutput(RequestManager.getInstance().send(payload)); return this.formatOutput(RequestManager.getInstance().send(payload));

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

@ -63,16 +63,23 @@ Property.prototype.formatOutput = function (result) {
Property.prototype.attachToObject = function (obj) { Property.prototype.attachToObject = function (obj) {
var proto = { var proto = {
get: this.get.bind(this), get: this.get.bind(this),
set: this.set.bind(this)
}; };
var name = this.name.split('.'); var names = this.name.split('.');
if (name.length > 1) { var name = names[0];
obj[name[0]] = obj[name[0]] || {}; if (names.length > 1) {
Object.defineProperty(obj[name[0]], name[1], proto); obj[names[0]] = obj[names[0]] || {};
} else { obj = obj[names[0]];
Object.defineProperty(obj, name[0], proto); name = names[1];
} }
Object.defineProperty(obj, name, proto);
var toAsyncName = function (prefix, name) {
return prefix + name.charAt(0).toUpperCase() + name.slice(1);
};
obj[toAsyncName('get', name)] = this.getAsync.bind(this);
}; };
/** /**
@ -88,15 +95,20 @@ Property.prototype.get = function () {
}; };
/** /**
* Should be used to set value of the property * Should be used to asynchrounously get value of property
* *
* @method set * @method getAsync
* @param {Object} new value of the property * @param {Function}
*/ */
Property.prototype.set = function (value) { Property.prototype.getAsync = function (callback) {
return RequestManager.getInstance().send({ var self = this;
method: this.setter, RequestManager.getInstance().sendAsync({
params: [this.formatInput(value)] method: this.getter
}, function (err, result) {
if (err) {
return callback(err);
}
callback(err, self.formatOutput(result));
}); });
}; };

27
libjsqrc/ethereumjs/lib/web3/requestmanager.js

@ -105,6 +105,33 @@ RequestManager.prototype.sendAsync = function (data, callback) {
}); });
}; };
/**
* Should be called to asynchronously send batch request
*
* @method sendBatch
* @param {Array} batch data
* @param {Function} callback
*/
RequestManager.prototype.sendBatch = function (data, callback) {
if (!this.provider) {
return callback(errors.InvalidProvider());
}
var payload = Jsonrpc.getInstance().toBatchPayload(data);
this.provider.sendAsync(payload, function (err, results) {
if (err) {
return callback(err);
}
if (!utils.isArray(results)) {
return callback(errors.InvalidResponse(results));
}
callback(err, results);
});
};
/** /**
* Should be used to set provider of request manager * Should be used to set provider of request manager
* *

15
libjsqrc/ethereumjs/lib/web3/watches.js

@ -25,7 +25,20 @@ var Method = require('./method');
/// @returns an array of objects describing web3.eth.filter api methods /// @returns an array of objects describing web3.eth.filter api methods
var eth = function () { var eth = function () {
var newFilterCall = function (args) { var newFilterCall = function (args) {
return typeof args[0] === 'string' ? 'eth_newBlockFilter' : 'eth_newFilter'; var type = args[0];
switch(type) {
case 'latest':
args.pop();
this.params = 0;
return 'eth_newBlockFilter';
case 'pending':
args.pop();
this.params = 0;
return 'eth_newPendingTransactionFilter';
default:
return 'eth_newFilter';
}
}; };
var newFilter = new Method({ var newFilter = new Method({

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.3.6', version: '0.4.2',
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.

2
libjsqrc/ethereumjs/package.json

@ -1,7 +1,7 @@
{ {
"name": "web3", "name": "web3",
"namespace": "ethereum", "namespace": "ethereum",
"version": "0.3.6", "version": "0.4.2",
"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": {

106
libjsqrc/ethereumjs/test/abi.formatConstructorParams.js

@ -1,106 +0,0 @@
var chai = require('chai');
var assert = require('assert');
var abi = require('../lib/solidity/abi');
describe('lib/solidity/abi', function () {
describe('formatConstructorParams', function () {
it('should format uint256 properly', function () {
// given
var description = [{
"name": "test",
"type": "constructor",
"inputs": [{
"name": "a",
"type": "uint256"
}
]
}];
// when
var bytes = abi.formatConstructorParams(description, [2]);
// then
assert.equal(bytes, '0000000000000000000000000000000000000000000000000000000000000002');
});
it('should not find matching constructor', function () {
// given
var description = [{
"name": "test",
"type": "constructor",
"inputs": [{
"name": "a",
"type": "uint256"
}
]
}];
// when
var bytes = abi.formatConstructorParams(description, []);
// then
assert.equal(bytes, '');
});
it('should not find matching constructor2', function () {
// given
var description = [{
"name": "test",
"type": "constructor",
"inputs": [{
"name": "a",
"type": "uint256"
}
]
}];
// when
var bytes = abi.formatConstructorParams(description, [1,2]);
// then
assert.equal(bytes, '');
});
it('should not find matching constructor3', function () {
// given
var description = [{
"name": "test",
"type": "function",
"inputs": [{
"name": "a",
"type": "uint256"
}
]
}];
// when
var bytes = abi.formatConstructorParams(description, [2]);
// then
assert.equal(bytes, '');
});
it('should find matching constructor with multiple args', function () {
// given
var description = [{
"name": "test",
"type": "constructor",
"inputs": [{
"name": "a",
"type": "uint256"
}, {
"name": "b",
"type": "uint256"
}]
}];
// when
var bytes = abi.formatConstructorParams(description, ['1', '5']);
// then
assert.equal(bytes, '00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000005');
});
});
});

69
libjsqrc/ethereumjs/test/async.js

@ -0,0 +1,69 @@
var chai = require('chai');
var assert = chai.assert;
var web3 = require('../index');
var FakeHttpProvider = require('./helpers/FakeHttpProvider');
// use sendTransaction as dummy
var method = 'sendTransaction';
var tests = [{
result: '0xb',
formattedResult: '0xb',
call: 'eth_'+ method
}];
describe('async', function () {
tests.forEach(function (test, index) {
it('test: ' + index, function (done) {
// given
var provider = new FakeHttpProvider();
web3.setProvider(provider);
provider.injectResult(test.result);
provider.injectValidation(function (payload) {
assert.equal(payload.jsonrpc, '2.0');
assert.equal(payload.method, test.call);
assert.deepEqual(payload.params, [{}]);
});
// when
web3.eth[method]({}, function(error, result){
// then
assert.isNull(error);
assert.strictEqual(test.formattedResult, result);
done();
});
});
it('error test: ' + index, function (done) {
// given
var provider = new FakeHttpProvider();
web3.setProvider(provider);
provider.injectError({
message: test.result,
code: -32603
});
provider.injectValidation(function (payload) {
assert.equal(payload.jsonrpc, '2.0');
assert.equal(payload.method, test.call);
assert.deepEqual(payload.params, [{}]);
});
// when
web3.eth[method]({}, function(error, result){
// then
assert.isUndefined(result);
assert.strictEqual(test.formattedResult, error.message);
done();
});
});
});
});

86
libjsqrc/ethereumjs/test/batch.js

@ -0,0 +1,86 @@
var chai = require('chai');
var assert = chai.assert;
var web3 = require('../index');
var FakeHttpProvider = require('./helpers/FakeHttpProvider');
var bn = require('bignumber.js');
describe('lib/web3/batch', function () {
describe('execute', function () {
it('should execute batch request', function (done) {
var provider = new FakeHttpProvider();
web3.setProvider(provider);
web3.reset();
var result = '0x126';
var result2 = '0x127';
provider.injectBatchResults([result, result2]);
var counter = 0;
var callback = function (err, r) {
counter++;
assert.deepEqual(new bn(result), r);
};
var callback2 = function (err, r) {
assert.equal(counter, 1);
assert.deepEqual(new bn(result2), r);
done();
};
var batch = web3.createBatch();
batch.add(web3.eth.getBalance.request('0x0000000000000000000000000000000000000000', 'latest', callback));
batch.add(web3.eth.getBalance.request('0x0000000000000000000000000000000000000005', 'latest', callback2));
batch.execute();
});
it('should execute batch request', function (done) {
var provider = new FakeHttpProvider();
web3.setProvider(provider);
web3.reset();
var abi = [{
"name": "balance(address)",
"type": "function",
"inputs": [{
"name": "who",
"type": "address"
}],
"constant": true,
"outputs": [{
"name": "value",
"type": "uint256"
}]
}];
var address = '0x0000000000000000000000000000000000000000';
var result = '0x126';
var result2 = '0x0000000000000000000000000000000000000000000000000000000000000123';
var signature = '0x001122334455';
// TODO: fix this, maybe in browser sha3?
provider.injectResult(signature);
var counter = 0;
var callback = function (err, r) {
counter++;
assert.deepEqual(new bn(result), r);
};
var callback2 = function (err, r) {
assert.equal(counter, 1);
assert.deepEqual(new bn(result2), r);
done();
};
var batch = web3.createBatch();
batch.add(web3.eth.getBalance.request('0x0000000000000000000000000000000000000000', 'latest', callback));
batch.add(web3.eth.contract(abi).at(address).balance.request(address, callback2));
provider.injectBatchResults([result, result2]);
batch.execute();
});
});
});

100
libjsqrc/ethereumjs/test/contract.js

@ -116,8 +116,7 @@ describe('web3.eth.contract', function () {
} }
}); });
var Contract = web3.eth.contract(desc); var contract = web3.eth.contract(desc).at(address);
var contract = new Contract(address);
var res = 0; var res = 0;
contract.Changed({from: address}).watch(function(err, result) { contract.Changed({from: address}).watch(function(err, result) {
@ -155,8 +154,7 @@ describe('web3.eth.contract', function () {
} }
}); });
var Contract = web3.eth.contract(desc); var contract = web3.eth.contract(desc).at(address);
var contract = new Contract(address);
contract.balance(address); contract.balance(address);
}); });
@ -186,8 +184,7 @@ describe('web3.eth.contract', function () {
} }
}); });
var Contract = web3.eth.contract(desc); var contract = web3.eth.contract(desc).at(address);
var contract = new Contract(address);
contract.send(address, 17); contract.send(address, 17);
}); });
@ -218,8 +215,7 @@ describe('web3.eth.contract', function () {
} }
}); });
var Contract = web3.eth.contract(desc); var contract = web3.eth.contract(desc).at(address);
var contract = new Contract(address);
contract.balance(address, {from: address, gas: 50000}); contract.balance(address, {from: address, gas: 50000});
@ -251,8 +247,7 @@ describe('web3.eth.contract', function () {
} }
}); });
var Contract = web3.eth.contract(desc); var contract = web3.eth.contract(desc).at(address);
var contract = new Contract(address);
contract.balance.call(address, {from: address, gas: 50000}); contract.balance.call(address, {from: address, gas: 50000});
@ -287,8 +282,7 @@ describe('web3.eth.contract', function () {
} }
}); });
var Contract = web3.eth.contract(desc); var contract = web3.eth.contract(desc).at(address);
var contract = new Contract(address);
contract.send(address, 17, {from: address, gas: 50000, gasPrice: 3000, value: 10000}); contract.send(address, 17, {from: address, gas: 50000, gasPrice: 3000, value: 10000});
}); });
@ -322,12 +316,48 @@ describe('web3.eth.contract', function () {
} }
}); });
var Contract = web3.eth.contract(desc); var contract = web3.eth.contract(desc).at(address);
var contract = new Contract(address);
contract.send.sendTransaction(address, 17, {from: address, gas: 50000, gasPrice: 3000, value: 10000}); contract.send.sendTransaction(address, 17, {from: address, gas: 50000, gasPrice: 3000, value: 10000});
}); });
it('should explicitly sendTransaction with optional params and call callback without error', function (done) {
var provider = new FakeHttpProvider();
web3.setProvider(provider);
web3.reset();
var sha3 = '0x5131231231231231231231';
var address = '0x1234567890123456789012345678901234567890';
provider.injectResult(sha3);
var step = 0;
provider.injectValidation(function (payload) {
if (step === 0) {
step = 1;
assert.equal(payload.jsonrpc, '2.0');
assert.equal(payload.method, 'web3_sha3');
assert.equal(payload.params[0], web3.fromAscii('send(address,uint256)'));
} else if (step === 1) {
assert.equal(payload.method, 'eth_sendTransaction');
assert.deepEqual(payload.params, [{
data: sha3.slice(0, 10) +
'0000000000000000000000001234567890123456789012345678901234567890' +
'0000000000000000000000000000000000000000000000000000000000000011' ,
to: address,
from: address,
gas: '0xc350',
gasPrice: '0xbb8',
value: '0x2710'
}]);
}
});
var contract = web3.eth.contract(desc).at(address);
contract.send.sendTransaction(address, 17, {from: address, gas: 50000, gasPrice: 3000, value: 10000}, function (err) {
assert.equal(err, null);
done();
});
});
it('should call testArr method and properly parse result', function () { it('should call testArr method and properly parse result', function () {
var provider = new FakeHttpProvider2(); var provider = new FakeHttpProvider2();
web3.setProvider(provider); web3.setProvider(provider);
@ -356,12 +386,48 @@ describe('web3.eth.contract', function () {
step++; step++;
}); });
var Contract = web3.eth.contract(desc); var contract = web3.eth.contract(desc).at(address);
var contract = new Contract(address);
var result = contract.testArr([3]); var result = contract.testArr([3]);
assert.deepEqual(new BigNumber(5), result); assert.deepEqual(new BigNumber(5), result);
}); });
it('should call testArr method, properly parse result and return the result async', function (done) {
var provider = new FakeHttpProvider2();
web3.setProvider(provider);
web3.reset();
var sha3 = '0x5131231231231231231231';
var address = '0x1234567890123456789012345678901234567890';
provider.injectResultList([{
result: sha3
}, {
result: '0x0000000000000000000000000000000000000000000000000000000000000005'
}]);
var step = 0;
provider.injectValidation(function (payload) {
if (step === 1) { // getting sha3 is first
assert.equal(payload.method, 'eth_call');
assert.deepEqual(payload.params, [{
data: sha3.slice(0, 10) +
'0000000000000000000000000000000000000000000000000000000000000020' +
'0000000000000000000000000000000000000000000000000000000000000001' +
'0000000000000000000000000000000000000000000000000000000000000003',
to: address
},
'latest'
]);
}
step++;
});
var contract = web3.eth.contract(desc).at(address);
contract.testArr([3], function (err, result) {
assert.deepEqual(new BigNumber(5), result);
done();
});
});
}); });
}); });

23
libjsqrc/ethereumjs/test/method.request.js

@ -0,0 +1,23 @@
var chai = require('chai');
var assert = chai.assert;
var web3 = require('../index');
describe('lib/web3/method', function () {
describe('request', function () {
it('should create proper request', function () {
var callback = function (err, result) {};
var expected = {
method: 'eth_getBalance',
callback: callback,
params: ['0x0000000000000000000000000000000000000000', 'latest'],
};
var request = web3.eth.getBalance.request('0x0000000000000000000000000000000000000000', 'latest', callback);
expected.format = request.format;
assert.deepEqual(request, expected);
});
});
});

2
libjsqrc/ethereumjs/test/node/app.js

@ -1,4 +1,4 @@
var web3 = require('ethereum.js'); var web3 = require('web3');
console.log(web3.version.api); console.log(web3.version.api);

2
libjsqrc/ethereumjs/test/node/package.json

@ -9,6 +9,6 @@
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"ethereum.js": "ethereum/ethereum.js#master" "web3": "ethereum/web3.js#master"
} }
} }

21
libjsqrc/ethereumjs/test/polling.js

@ -6,15 +6,26 @@ var utils = require('../lib/utils/utils');
var tests = [{ var tests = [{
protocol: 'eth', protocol: 'eth',
args: ['pending'], args: ['latest'],
firstResult: 1, firstResult: 1,
firstPayload: { firstPayload: {
method: "eth_newBlockFilter", method: "eth_newBlockFilter",
params: [ params: []
"pending" },
] secondResult: ['0x1234'],
secondPayload: {
method: "eth_getFilterChanges"
}
},
{
protocol: 'eth',
args: ['pending'],
firstResult: 1,
firstPayload: {
method: "eth_newPendingTransactionFilter",
params: []
}, },
secondResult: [null], secondResult: ['0x1234'],
secondPayload: { secondPayload: {
method: "eth_getFilterChanges" method: "eth_getFilterChanges"
} }

20
libjsqrc/ethereumjs/test/web3.eth.blockNumber.js

@ -32,6 +32,26 @@ describe('web3.eth', function () {
// then // then
assert.strictEqual(test.formattedResult, result); assert.strictEqual(test.formattedResult, result);
}); });
it('async get property test: ' + index, function (done) {
// given
var provider = new FakeHttpProvider();
web3.setProvider(provider);
provider.injectResult(test.result);
provider.injectValidation(function (payload) {
assert.equal(payload.jsonrpc, '2.0');
assert.equal(payload.method, test.call);
assert.deepEqual(payload.params, []);
});
// when
web3.eth.getBlockNumber(function (err, result) {
assert.strictEqual(test.formattedResult, result);
done();
});
});
}); });
}); });
}); });

41
libjsqrc/ethereumjs/test/web3.eth.call.js

@ -0,0 +1,41 @@
var web3 = require('../index');
var testMethod = require('./helpers/test.method.js');
var method = 'call';
var tests = [{
args: [{
to: '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
data: '0x23455654',
gas: 11,
gasPrice: 11
}],
formattedArgs: [{
to: '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
data: '0x23455654',
gas: '0xb',
gasPrice: '0xb'
}, 'latest'],
result: '0x31981',
formattedResult: '0x31981',
call: 'eth_'+ method
},{
args: [{
to: '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
data: '0x23455654',
gas: 11,
gasPrice: 11
}, 11],
formattedArgs: [{
to: '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
data: '0x23455654',
gas: '0xb',
gasPrice: '0xb'
}, '0xb'],
result: '0x31981',
formattedResult: '0x31981',
call: 'eth_'+ method
}];
testMethod.runTests('eth', method, tests);

21
libjsqrc/ethereumjs/test/web3.eth.contract.js

@ -25,8 +25,7 @@ describe('web3.eth.contract', function() {
var address = '0x1234567890123456789012345678901234567890'; var address = '0x1234567890123456789012345678901234567890';
// when // when
var Con = contract(description); var myCon = contract(description).at(address);
var myCon = new Con(address);
// then // then
assert.equal('function', typeof myCon.test); assert.equal('function', typeof myCon.test);
@ -54,8 +53,7 @@ describe('web3.eth.contract', function() {
var address = '0x1234567890123456789012345678901234567890'; var address = '0x1234567890123456789012345678901234567890';
// when // when
var Con = contract(description); var myCon = contract(description).at(address);
var myCon = new Con(address);
// then // then
assert.equal('function', typeof myCon.test); assert.equal('function', typeof myCon.test);
@ -97,8 +95,7 @@ describe('web3.eth.contract', function() {
var address = '0x1234567890123456789012345678901234567890'; var address = '0x1234567890123456789012345678901234567890';
// when // when
var Con = contract(description); var myCon = contract(description).at(address);
var myCon = new Con(address);
// then // then
assert.equal('function', typeof myCon.test); assert.equal('function', typeof myCon.test);
@ -142,8 +139,7 @@ describe('web3.eth.contract', function() {
var address = '0x1234567890123456789012345678901234567890'; var address = '0x1234567890123456789012345678901234567890';
// when // when
var Con = contract(description); var myCon = contract(description).at(address);
var myCon = new Con(address);
// then // then
assert.equal('function', typeof myCon.test); assert.equal('function', typeof myCon.test);
@ -171,8 +167,7 @@ describe('web3.eth.contract', function() {
var address = '0x1234567890123456789012345678901234567890'; var address = '0x1234567890123456789012345678901234567890';
// when // when
var Con = contract(description); var myCon = contract(description).at(address);
var myCon = new Con(address);
// then // then
assert.equal('undefined', typeof myCon.test); assert.equal('undefined', typeof myCon.test);
@ -200,8 +195,7 @@ describe('web3.eth.contract', function() {
var address = '0x1234567890123456789012345678901234567890'; var address = '0x1234567890123456789012345678901234567890';
// when // when
var Con = contract(description); var myCon = contract(description).at(address);
var myCon = new Con(address);
// then // then
assert.equal('function', typeof myCon.test); assert.equal('function', typeof myCon.test);
@ -233,8 +227,7 @@ describe('web3.eth.contract', function() {
done(); done();
}); });
var Con = contract(description); var myCon = contract(description).new(2, {data: code});
var myCon = new Con({data: code}, 2);
}); });
}); });

25
libjsqrc/ethereumjs/test/web3.eth.estimateGas.js

@ -0,0 +1,25 @@
var web3 = require('../index');
var testMethod = require('./helpers/test.method.js');
var method = 'estimateGas';
var tests = [{
args: [{
to: '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
data: '0x23455654',
gas: 11,
gasPrice: 11
}],
formattedArgs: [{
to: '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
data: '0x23455654',
gas: '0xb',
gasPrice: '0xb'
}],
result: '0x31981',
formattedResult: 203137,
call: 'eth_'+ method
}];
testMethod.runTests('eth', method, tests);

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

Loading…
Cancel
Save