Browse Source

Merge branch 'develop' into evmjit-develop

cl-refactor
Paweł Bylica 10 years ago
parent
commit
ef1b9e45e1
  1. 14
      CMakeLists.txt
  2. 1
      alethzero/DappLoader.cpp
  3. 3
      alethzero/DappLoader.h
  4. 9
      alethzero/Main.ui
  5. 29
      alethzero/MainWin.cpp
  6. 15
      alethzero/OurWebThreeStubServer.cpp
  7. 9
      alethzero/OurWebThreeStubServer.h
  8. 8
      appdmg.json.in
  9. BIN
      bg.png
  10. 202
      eth/main.cpp
  11. 120
      ethminer/MinerAux.h
  12. BIN
      install-folder-bg.png
  13. BIN
      install-folder-bg@2x.png
  14. 12
      libdevcore/Base64.cpp
  15. 2
      libdevcore/Common.cpp
  16. 9
      libdevcore/FixedHash.h
  17. 74
      libethash-cl/ethash_cl_miner.cpp
  18. 18
      libethash-cl/ethash_cl_miner.h
  19. 2
      libethash-cl/ethash_cl_miner_kernel.cl
  20. 25
      libethcore/Common.h
  21. 16
      libethcore/Ethash.cpp
  22. 16
      libethcore/Ethash.h
  23. 5
      libethcore/EthashAux.cpp
  24. 1
      libethcore/EthashAux.h
  25. 10
      libethereum/BlockChain.cpp
  26. 2
      libethereum/BlockChain.h
  27. 123
      libethereum/Client.cpp
  28. 8
      libethereum/Client.h
  29. 18
      libethereum/ClientBase.cpp
  30. 1
      libethereum/ClientBase.h
  31. 25
      libethereum/CommonNet.h
  32. 5
      libethereum/DownloadMan.h
  33. 137
      libethereum/EthereumHost.cpp
  34. 32
      libethereum/EthereumHost.h
  35. 10
      libethereum/EthereumPeer.cpp
  36. 3
      libethereum/EthereumPeer.h
  37. 2
      libethereum/Executive.h
  38. 59
      libethereum/ExtVM.cpp
  39. 25
      libethereum/State.cpp
  40. 12
      libethereum/State.h
  41. 27
      libevm/ExtVMFace.h
  42. 48
      libevmasm/CommonSubexpressionEliminator.cpp
  43. 5586
      libjsqrc/ethereumjs/dist/web3-light.js
  44. 2
      libjsqrc/ethereumjs/dist/web3-light.min.js
  45. 27
      libjsqrc/ethereumjs/dist/web3.js
  46. 4
      libjsqrc/ethereumjs/dist/web3.min.js
  47. 2
      libjsqrc/ethereumjs/gulpfile.js
  48. 13
      libjsqrc/ethereumjs/lib/web3.js
  49. 10
      libjsqrc/ethereumjs/lib/web3/eth.js
  50. 1
      libjsqrc/ethereumjs/lib/web3/property.js
  51. 96
      libp2p/Host.cpp
  52. 9
      libp2p/Host.h
  53. 2
      libp2p/Network.h
  54. 12
      libp2p/NodeTable.cpp
  55. 4
      libp2p/NodeTable.h
  56. 2
      libp2p/Peer.cpp
  57. 30
      libp2p/RLPXFrameCoder.cpp
  58. 51
      libp2p/RLPXFrameCoder.h
  59. 0
      libp2p/RLPXSocket.cpp
  60. 56
      libp2p/RLPXSocket.h
  61. 6
      libp2p/RLPxHandshake.cpp
  62. 9
      libp2p/RLPxHandshake.h
  63. 35
      libp2p/Session.cpp
  64. 11
      libp2p/Session.h
  65. 38
      libsolidity/AST.cpp
  66. 83
      libsolidity/AST.h
  67. 50
      libsolidity/Compiler.cpp
  68. 7
      libsolidity/Compiler.h
  69. 29
      libsolidity/ExpressionCompiler.cpp
  70. 2
      libsolidity/InterfaceHandler.cpp
  71. 6
      libsolidity/LValue.cpp
  72. 14
      libsolidity/NameAndTypeResolver.cpp
  73. 158
      libsolidity/Types.cpp
  74. 103
      libsolidity/Types.h
  75. 4
      libtestutils/FixedClient.cpp
  76. 427
      libweb3jsonrpc/JsonHelper.cpp
  77. 104
      libweb3jsonrpc/JsonHelper.h
  78. 268
      libweb3jsonrpc/WebThreeStubServer.cpp
  79. 47
      libweb3jsonrpc/WebThreeStubServer.h
  80. 401
      libweb3jsonrpc/WebThreeStubServerBase.cpp
  81. 53
      libweb3jsonrpc/WebThreeStubServerBase.h
  82. 138
      libweb3jsonrpc/abstractwebthreestubserver.h
  83. 27
      libweb3jsonrpc/spec.json
  84. 11
      libwebthree/WebThree.cpp
  85. 2
      libwebthree/WebThree.h
  86. 259
      mix/ClientModel.cpp
  87. 41
      mix/ClientModel.h
  88. 7
      mix/ContractCallDataEncoder.cpp
  89. 2
      mix/ContractCallDataEncoder.h
  90. 84
      mix/MachineStates.h
  91. 95
      mix/MixClient.cpp
  92. 2
      mix/MixClient.h
  93. 17
      mix/QContractDefinition.cpp
  94. 8
      mix/QContractDefinition.h
  95. 31
      mix/QFunctionDefinition.cpp
  96. 7
      mix/QFunctionDefinition.h
  97. 16
      mix/QVariableDeclaration.cpp
  98. 9
      mix/QVariableDeclaration.h
  99. 5
      mix/qml.qrc
  100. 15
      mix/qml/Application.qml

14
CMakeLists.txt

@ -37,7 +37,7 @@ option(ETHKEY "Build the CLI key manager component" ON)
option(SOLIDITY "Build the Solidity language components" ON) option(SOLIDITY "Build the Solidity language components" ON)
option(SERPENT "Build the Serpent language components" ON) option(SERPENT "Build the Serpent language components" ON)
option(TOOLS "Build the tools components" ON) option(TOOLS "Build the tools components" ON)
option(NCURSES "Build the NCurses components" ON) option(NCURSES "Build the NCurses components" OFF)
option(GUI "Build GUI components (AlethZero, Mix)" ON) option(GUI "Build GUI components (AlethZero, Mix)" ON)
option(TESTS "Build the tests." ON) option(TESTS "Build the tests." ON)
option(EVMJIT "Build just-in-time compiler for EVM code (requires LLVM)" OFF) option(EVMJIT "Build just-in-time compiler for EVM code (requires LLVM)" OFF)
@ -222,7 +222,7 @@ elseif (BUNDLE STREQUAL "full")
set(SOLIDITY ON) set(SOLIDITY ON)
set(USENPM ON) set(USENPM ON)
set(GUI ON) set(GUI ON)
set(NCURSES ${DECENT_PLATFORM}) # set(NCURSES ${DECENT_PLATFORM})
set(TOOLS ON) set(TOOLS ON)
set(TESTS ON) set(TESTS ON)
set(FATDB ON) set(FATDB ON)
@ -249,7 +249,7 @@ elseif (BUNDLE STREQUAL "user")
set(SOLIDITY OFF) set(SOLIDITY OFF)
set(USENPM OFF) set(USENPM OFF)
set(GUI ON) set(GUI ON)
set(NCURSES ${DECENT_PLATFORM}) # set(NCURSES ${DECENT_PLATFORM})
set(TOOLS ON) set(TOOLS ON)
set(TESTS OFF) set(TESTS OFF)
elseif (BUNDLE STREQUAL "wallet") elseif (BUNDLE STREQUAL "wallet")
@ -430,9 +430,9 @@ if (TOOLS)
endif() endif()
if (NCURSES) #if (NCURSES)
add_subdirectory(neth) # add_subdirectory(neth)
endif () #endif ()
if (GUI) if (GUI)
@ -458,7 +458,7 @@ if (APPLE AND GUI)
-DAPP_DMG_EXE=${ETH_APP_DMG} -DAPP_DMG_EXE=${ETH_APP_DMG}
-DAPP_DMG_FILE=appdmg.json.in -DAPP_DMG_FILE=appdmg.json.in
-DAPP_DMG_ICON="alethzero/alethzero.icns" -DAPP_DMG_ICON="alethzero/alethzero.icns"
-DAPP_DMG_BACKGROUND="bg.png" -DAPP_DMG_BACKGROUND="install-folder-bg.png"
-DETH_BUILD_DIR="${CMAKE_BINARY_DIR}" -DETH_BUILD_DIR="${CMAKE_BINARY_DIR}"
-DETH_MIX_APP="$<TARGET_FILE_DIR:mix>" -DETH_MIX_APP="$<TARGET_FILE_DIR:mix>"
-DETH_ALETHZERO_APP="$<TARGET_FILE_DIR:AlethZero>" -DETH_ALETHZERO_APP="$<TARGET_FILE_DIR:AlethZero>"

1
alethzero/DappLoader.cpp

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

3
alethzero/DappLoader.h

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

9
alethzero/Main.ui

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

29
alethzero/MainWin.cpp

@ -198,6 +198,7 @@ Main::Main(QWidget *parent) :
statusBar()->addPermanentWidget(ui->balance); statusBar()->addPermanentWidget(ui->balance);
statusBar()->addPermanentWidget(ui->peerCount); statusBar()->addPermanentWidget(ui->peerCount);
statusBar()->addPermanentWidget(ui->mineStatus); statusBar()->addPermanentWidget(ui->mineStatus);
statusBar()->addPermanentWidget(ui->syncStatus);
statusBar()->addPermanentWidget(ui->chainStatus); statusBar()->addPermanentWidget(ui->chainStatus);
statusBar()->addPermanentWidget(ui->blockCount); statusBar()->addPermanentWidget(ui->blockCount);
@ -209,7 +210,9 @@ Main::Main(QWidget *parent) :
m_webThree.reset(new WebThreeDirect(string("AlethZero/v") + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), getDataDir(), WithExisting::Trust, {"eth"/*, "shh"*/}, p2p::NetworkPreferences(), network)); m_webThree.reset(new WebThreeDirect(string("AlethZero/v") + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), getDataDir(), WithExisting::Trust, {"eth"/*, "shh"*/}, p2p::NetworkPreferences(), network));
m_httpConnector.reset(new jsonrpc::HttpServer(SensibleHttpPort, "", "", dev::SensibleHttpThreads)); m_httpConnector.reset(new jsonrpc::HttpServer(SensibleHttpPort, "", "", dev::SensibleHttpThreads));
m_server.reset(new OurWebThreeStubServer(*m_httpConnector, *web3(), this)); auto w3ss = new OurWebThreeStubServer(*m_httpConnector, this);
m_server.reset(w3ss);
auto sessionKey = w3ss->newSession(SessionPermissions{{Priviledge::Admin}});
connect(&*m_server, SIGNAL(onNewId(QString)), SLOT(addNewId(QString))); connect(&*m_server, SIGNAL(onNewId(QString)), SLOT(addNewId(QString)));
m_server->setIdentities(keysAsVector(owned())); m_server->setIdentities(keysAsVector(owned()));
m_server->StartListening(); m_server->StartListening();
@ -226,12 +229,16 @@ Main::Main(QWidget *parent) :
m_dappHost.reset(new DappHost(8081)); m_dappHost.reset(new DappHost(8081));
m_dappLoader = new DappLoader(this, web3(), getNameReg()); m_dappLoader = new DappLoader(this, web3(), getNameReg());
m_dappLoader->setSessionKey(sessionKey);
connect(m_dappLoader, &DappLoader::dappReady, this, &Main::dappLoaded); connect(m_dappLoader, &DappLoader::dappReady, this, &Main::dappLoaded);
connect(m_dappLoader, &DappLoader::pageReady, this, &Main::pageLoaded); connect(m_dappLoader, &DappLoader::pageReady, this, &Main::pageLoaded);
// ui->webView->page()->settings()->setAttribute(QWebEngineSettings::DeveloperExtrasEnabled, true); // ui->webView->page()->settings()->setAttribute(QWebEngineSettings::DeveloperExtrasEnabled, true);
// QWebEngineInspector* inspector = new QWebEngineInspector(); // QWebEngineInspector* inspector = new QWebEngineInspector();
// inspector->setPage(page); // inspector->setPage(page);
setBeneficiary(*m_keyManager.accounts().begin()); setBeneficiary(*m_keyManager.accounts().begin());
ethereum()->setDefault(LatestBlock);
readSettings(); readSettings();
m_transact = new Transact(this, this); m_transact = new Transact(this, this);
@ -1245,9 +1252,15 @@ void Main::refreshBlockCount()
{ {
auto d = ethereum()->blockChain().details(); auto d = ethereum()->blockChain().details();
BlockQueueStatus b = ethereum()->blockQueueStatus(); BlockQueueStatus b = ethereum()->blockQueueStatus();
HashChainStatus h = ethereum()->hashChainStatus(); SyncStatus sync = ethereum()->syncStatus();
ui->chainStatus->setText(QString("%10/%11%12 hashes %3 importing %4 ready %5 verifying %6 unverified %7 future %8 unknown %9 bad %1 #%2") QString syncStatus = EthereumHost::stateName(sync.state);
.arg(m_privateChain.size() ? "[" + m_privateChain + "] " : "testnet").arg(d.number).arg(b.importing).arg(b.verified).arg(b.verifying).arg(b.unverified).arg(b.future).arg(b.unknown).arg(b.bad).arg(h.received).arg(h.estimated ? "~" : "").arg(h.total)); if (sync.state == SyncState::HashesParallel || sync.state == SyncState::HashesSingle)
syncStatus += QString(": %1/%2%3").arg(sync.hashesReceived).arg(sync.hashesEstimated ? "~" : "").arg(sync.hashesTotal);
if (sync.state == SyncState::Blocks || sync.state == SyncState::NewBlocks)
syncStatus += QString(": %1/%2").arg(sync.blocksReceived).arg(sync.blocksTotal);
ui->syncStatus->setText(syncStatus);
ui->chainStatus->setText(QString("%3 importing %4 ready %5 verifying %6 unverified %7 future %8 unknown %9 bad %1 #%2")
.arg(m_privateChain.size() ? "[" + m_privateChain + "] " : "testnet").arg(d.number).arg(b.importing).arg(b.verified).arg(b.verifying).arg(b.unverified).arg(b.future).arg(b.unknown).arg(b.bad));
} }
void Main::on_turboMining_triggered() void Main::on_turboMining_triggered()
@ -1909,7 +1922,7 @@ void Main::on_clearPending_triggered()
void Main::on_retryUnknown_triggered() void Main::on_retryUnknown_triggered()
{ {
ethereum()->retryUnkonwn(); ethereum()->retryUnknown();
} }
void Main::on_killBlockchain_triggered() void Main::on_killBlockchain_triggered()
@ -1928,11 +1941,7 @@ void Main::on_net_triggered()
{ {
ui->port->setEnabled(!ui->net->isChecked()); ui->port->setEnabled(!ui->net->isChecked());
ui->clientName->setEnabled(!ui->net->isChecked()); ui->clientName->setEnabled(!ui->net->isChecked());
string n = string("AlethZero/v") + dev::Version; web3()->setClientVersion(WebThreeDirect::composeClientVersion("AlethZero", ui->clientName->text().toStdString()));
if (ui->clientName->text().size())
n += "/" + ui->clientName->text().toStdString();
n += "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM);
web3()->setClientVersion(n);
if (ui->net->isChecked()) if (ui->net->isChecked())
{ {
web3()->setIdealPeerCount(ui->idealPeers->value()); web3()->setIdealPeerCount(ui->idealPeers->value());

15
alethzero/OurWebThreeStubServer.cpp

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

9
alethzero/OurWebThreeStubServer.h

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

8
appdmg.json.in

@ -2,11 +2,11 @@
"title": "Ethereum", "title": "Ethereum",
"icon": "appdmg_icon.icns", "icon": "appdmg_icon.icns",
"background": "appdmg_background.png", "background": "appdmg_background.png",
"icon-size": 80, "icon-size": 55,
"contents": [ "contents": [
{ "x": 600, "y": 170, "type": "link", "path": "/Applications" }, { "x": 242, "y": 240, "type": "link", "path": "/Applications" },
{ "x": 150, "y": 90, "type": "file", "path": "${ETH_ALETHZERO_APP}" }, { "x": 145, "y": 125, "type": "file", "path": "${ETH_ALETHZERO_APP}" },
{ "x": 150, "y": 260, "type": "file", "path": "${ETH_MIX_APP}" } { "x": 339, "y": 125, "type": "file", "path": "${ETH_MIX_APP}" }
] ]
} }

BIN
bg.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 171 KiB

202
eth/main.cpp

@ -93,6 +93,7 @@ void interactiveHelp()
<< " accounts Gives information on all owned accounts (balances, mining beneficiary and default signer)." << endl << " accounts Gives information on all owned accounts (balances, mining beneficiary and default signer)." << endl
<< " newaccount <name> Creates a new account with the given name." << endl << " newaccount <name> Creates a new account with the given name." << endl
<< " transact Execute a given transaction." << endl << " transact Execute a given transaction." << endl
<< " transactnonce Execute a given transaction with a specified nonce." << endl
<< " txcreate Execute a given contract creation transaction." << endl << " txcreate Execute a given contract creation transaction." << endl
<< " send Execute a given transaction with current secret." << endl << " send Execute a given transaction with current secret." << endl
<< " contract Create a new contract with current secret." << endl << " contract Create a new contract with current secret." << endl
@ -101,6 +102,9 @@ void interactiveHelp()
<< " listaccounts List the accounts on the network." << endl << " listaccounts List the accounts on the network." << endl
<< " listcontracts List the contracts on the network." << endl << " listcontracts List the contracts on the network." << endl
<< " balanceat <address> Gives the balance of the given account." << endl << " balanceat <address> Gives the balance of the given account." << endl
<< " balanceatblock <address> <blocknumber> Gives the balance of the given account." << endl
<< " storageat <address> Gives the storage of the given account." << endl
<< " storageatblock <address> <blocknumber> Gives the storahe of the given account at a given blocknumber." << endl
<< " codeat <address> Gives the code of the given account." << endl << " codeat <address> Gives the code of the given account." << endl
#endif #endif
<< " setsigningkey <addr> Set the address with which to sign transactions." << endl << " setsigningkey <addr> Set the address with which to sign transactions." << endl
@ -108,6 +112,7 @@ void interactiveHelp()
<< " exportconfig <path> Export the config (.RLP) to the path provided." << endl << " exportconfig <path> Export the config (.RLP) to the path provided." << endl
<< " importconfig <path> Import the config (.RLP) from the path provided." << endl << " importconfig <path> Import the config (.RLP) from the path provided." << endl
<< " inspect <contract> Dumps a contract to <APPDATA>/<contract>.evm." << endl << " inspect <contract> Dumps a contract to <APPDATA>/<contract>.evm." << endl
<< " reprocess <block> Reprocess a given block." << endl
<< " dumptrace <block> <index> <filename> <format> Dumps a transaction trace" << endl << "to <filename>. <format> should be one of pretty, standard, standard+." << endl << " dumptrace <block> <index> <filename> <format> Dumps a transaction trace" << endl << "to <filename>. <format> should be one of pretty, standard, standard+." << endl
<< " dumpreceipt <block> <index> Dumps a transation receipt." << endl << " dumpreceipt <block> <index> Dumps a transation receipt." << endl
<< " exit Exits the application." << endl; << " exit Exits the application." << endl;
@ -124,6 +129,7 @@ void help()
#if ETH_JSONRPC || !ETH_TRUE #if ETH_JSONRPC || !ETH_TRUE
<< " -j,--json-rpc Enable JSON-RPC server (default: off)." << endl << " -j,--json-rpc Enable JSON-RPC server (default: off)." << endl
<< " --json-rpc-port <n> Specify JSON-RPC server port (implies '-j', default: " << SensibleHttpPort << ")." << endl << " --json-rpc-port <n> Specify JSON-RPC server port (implies '-j', default: " << SensibleHttpPort << ")." << endl
<< " --admin <password> Specify admin session key for JSON-RPC (default: auto-generated and printed at startup)." << endl
#endif #endif
<< " -K,--kill First kill the blockchain." << endl << " -K,--kill First kill the blockchain." << endl
<< " -R,--rebuild Rebuild the blockchain from the existing database." << endl << " -R,--rebuild Rebuild the blockchain from the existing database." << endl
@ -165,6 +171,9 @@ 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
<< " --no-discovery Disable Node discovery. (experimental)" << endl
<< " --pin Only connect to required (trusted) peers. (experimental)" << endl
// << " --require-peers <peers.json> List of required (trusted) peers. (experimental)" << endl
<< endl; << endl;
MinerCLI::streamHelp(cout); MinerCLI::streamHelp(cout);
cout cout
@ -286,6 +295,7 @@ int main(int argc, char** argv)
#if ETH_JSONRPC #if ETH_JSONRPC
int jsonrpc = -1; int jsonrpc = -1;
#endif #endif
string jsonAdmin;
bool upnp = true; bool upnp = true;
WithExisting killChain = WithExisting::Trust; WithExisting killChain = WithExisting::Trust;
bool jit = false; bool jit = false;
@ -300,6 +310,8 @@ int main(int argc, char** argv)
unsigned short remotePort = 30303; unsigned short remotePort = 30303;
unsigned peers = 11; unsigned peers = 11;
bool bootstrap = false; bool bootstrap = false;
bool disableDiscovery = false;
bool pinning = false;
unsigned networkId = 0; unsigned networkId = 0;
/// Mining params /// Mining params
@ -588,6 +600,10 @@ int main(int argc, char** argv)
} }
else if (arg == "-b" || arg == "--bootstrap") else if (arg == "-b" || arg == "--bootstrap")
bootstrap = true; bootstrap = true;
else if (arg == "--no-discovery")
disableDiscovery = true;
else if (arg == "--pin")
pinning = true;
else if (arg == "-f" || arg == "--force-mining") else if (arg == "-f" || arg == "--force-mining")
forceMining = true; forceMining = true;
else if (arg == "-i" || arg == "--interactive") else if (arg == "-i" || arg == "--interactive")
@ -597,6 +613,8 @@ int main(int argc, char** argv)
jsonrpc = jsonrpc == -1 ? SensibleHttpPort : jsonrpc; jsonrpc = jsonrpc == -1 ? SensibleHttpPort : jsonrpc;
else if (arg == "--json-rpc-port" && i + 1 < argc) else if (arg == "--json-rpc-port" && i + 1 < argc)
jsonrpc = atoi(argv[++i]); jsonrpc = atoi(argv[++i]);
else if (arg == "--json-admin" && i + 1 < argc)
jsonAdmin = argv[++i];
#endif #endif
#if ETH_JSCONSOLE #if ETH_JSCONSOLE
else if (arg == "--console") else if (arg == "--console")
@ -678,10 +696,11 @@ int main(int argc, char** argv)
StructuredLogger::get().initialize(structuredLogging, structuredLoggingFormat, structuredLoggingURL); StructuredLogger::get().initialize(structuredLogging, structuredLoggingFormat, structuredLoggingURL);
VMFactory::setKind(jit ? VMKind::JIT : VMKind::Interpreter); VMFactory::setKind(jit ? VMKind::JIT : VMKind::Interpreter);
auto netPrefs = publicIP.empty() ? NetworkPreferences(listenIP ,listenPort, upnp) : NetworkPreferences(publicIP, listenIP ,listenPort, upnp); auto netPrefs = publicIP.empty() ? NetworkPreferences(listenIP ,listenPort, upnp) : NetworkPreferences(publicIP, listenIP ,listenPort, upnp);
netPrefs.discovery = !disableDiscovery;
netPrefs.pin = pinning;
auto nodesState = contents((dbPath.size() ? dbPath : getDataDir()) + "/network.rlp"); auto nodesState = contents((dbPath.size() ? dbPath : getDataDir()) + "/network.rlp");
std::string clientImplString = "++eth/" + clientName + "v" + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM) + (jit ? "/JIT" : "");
dev::WebThreeDirect web3( dev::WebThreeDirect web3(
clientImplString, WebThreeDirect::composeClientVersion("++eth", clientName),
dbPath, dbPath,
killChain, killChain,
nodeMode == NodeMode::Full ? set<string>{"eth"/*, "shh"*/} : set<string>(), nodeMode == NodeMode::Full ? set<string>{"eth"/*, "shh"*/} : set<string>(),
@ -794,7 +813,7 @@ int main(int argc, char** argv)
// 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));
std::shared_ptr<eth::TrivialGasPricer> gasPricer = make_shared<eth::TrivialGasPricer>(askPrice, bidPrice); std::shared_ptr<eth::TrivialGasPricer> gasPricer = make_shared<eth::TrivialGasPricer>(askPrice, bidPrice);
eth::Client* c = nodeMode == NodeMode::Full ? web3.ethereum() : nullptr; eth::Client* c = nodeMode == NodeMode::Full ? web3.ethereum() : nullptr;
StructuredLogger::starting(clientImplString, dev::Version); StructuredLogger::starting(WebThreeDirect::composeClientVersion("++eth", clientName), dev::Version);
if (c) if (c)
{ {
c->setGasPricer(gasPricer); c->setGasPricer(gasPricer);
@ -806,14 +825,14 @@ int main(int argc, char** argv)
cout << "Transaction Signer: " << signingKey << endl; cout << "Transaction Signer: " << signingKey << endl;
cout << "Mining Benefactor: " << beneficiary << endl; cout << "Mining Benefactor: " << beneficiary << endl;
web3.startNetwork();
cout << "Node ID: " << web3.enode() << endl;
if (bootstrap) if (bootstrap || !remoteHost.empty())
for (auto const& i: Host::pocHosts()) {
web3.requirePeer(i.first, i.second); web3.startNetwork();
if (remoteHost.size()) cout << "Node ID: " << web3.enode() << endl;
web3.addNode(p2p::NodeId(), remoteHost + ":" + toString(remotePort)); }
else
cout << "Networking disabled. To start, use netstart or pass -b or a remote host." << endl;
#if ETH_JSONRPC || !ETH_TRUE #if ETH_JSONRPC || !ETH_TRUE
shared_ptr<WebThreeStubServer> jsonrpcServer; shared_ptr<WebThreeStubServer> jsonrpcServer;
@ -821,11 +840,22 @@ int main(int argc, char** argv)
if (jsonrpc > -1) if (jsonrpc > -1)
{ {
jsonrpcConnector = unique_ptr<jsonrpc::AbstractServerConnector>(new jsonrpc::HttpServer(jsonrpc, "", "", SensibleHttpThreads)); jsonrpcConnector = unique_ptr<jsonrpc::AbstractServerConnector>(new jsonrpc::HttpServer(jsonrpc, "", "", SensibleHttpThreads));
jsonrpcServer = shared_ptr<WebThreeStubServer>(new WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared<SimpleAccountHolder>([&](){return web3.ethereum();}, getAccountPassword, keyManager), vector<KeyPair>())); jsonrpcServer = shared_ptr<WebThreeStubServer>(new WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared<SimpleAccountHolder>([&](){ return web3.ethereum(); }, getAccountPassword, keyManager), vector<KeyPair>(), keyManager, *gasPricer));
jsonrpcServer->StartListening(); jsonrpcServer->StartListening();
if (jsonAdmin.empty())
jsonAdmin = jsonrpcServer->newSession(SessionPermissions{{Priviledge::Admin}});
else
jsonrpcServer->addSession(jsonAdmin, SessionPermissions{{Priviledge::Admin}});
cout << "JSONRPC Admin Session Key: " << jsonAdmin << endl;
} }
#endif #endif
if (bootstrap)
for (auto const& i: Host::pocHosts())
web3.requirePeer(i.first, i.second);
if (!remoteHost.empty())
web3.addNode(p2p::NodeId(), remoteHost + ":" + toString(remotePort));
signal(SIGABRT, &sighandler); signal(SIGABRT, &sighandler);
signal(SIGTERM, &sighandler); signal(SIGTERM, &sighandler);
signal(SIGINT, &sighandler); signal(SIGINT, &sighandler);
@ -965,8 +995,13 @@ int main(int argc, char** argv)
if (jsonrpc < 0) if (jsonrpc < 0)
jsonrpc = SensibleHttpPort; jsonrpc = SensibleHttpPort;
jsonrpcConnector = unique_ptr<jsonrpc::AbstractServerConnector>(new jsonrpc::HttpServer(jsonrpc, "", "", SensibleHttpThreads)); jsonrpcConnector = unique_ptr<jsonrpc::AbstractServerConnector>(new jsonrpc::HttpServer(jsonrpc, "", "", SensibleHttpThreads));
jsonrpcServer = shared_ptr<WebThreeStubServer>(new WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared<SimpleAccountHolder>([&](){return web3.ethereum();}, getAccountPassword, keyManager), vector<KeyPair>())); jsonrpcServer = shared_ptr<WebThreeStubServer>(new WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared<SimpleAccountHolder>([&](){ return web3.ethereum(); }, getAccountPassword, keyManager), vector<KeyPair>(), keyManager, *gasPricer));
jsonrpcServer->StartListening(); jsonrpcServer->StartListening();
if (jsonAdmin.empty())
jsonAdmin = jsonrpcServer->newSession(SessionPermissions{{Priviledge::Admin}});
else
jsonrpcServer->addSession(jsonAdmin, SessionPermissions{{Priviledge::Admin}});
cout << "JSONRPC Admin Session Key: " << jsonAdmin << endl;
} }
else if (cmd == "jsonstop") else if (cmd == "jsonstop")
{ {
@ -1061,7 +1096,7 @@ int main(int argc, char** argv)
} }
else if (c && cmd == "retryunknown") else if (c && cmd == "retryunknown")
{ {
c->retryUnkonwn(); c->retryUnknown();
} }
else if (cmd == "peers") else if (cmd == "peers")
{ {
@ -1175,6 +1210,75 @@ int main(int argc, char** argv)
else else
cwarn << "Require parameters: submitTransaction ADDRESS AMOUNT GASPRICE GAS SECRET DATA"; cwarn << "Require parameters: submitTransaction ADDRESS AMOUNT GASPRICE GAS SECRET DATA";
} }
else if (c && cmd == "transactnonce")
{
auto const& bc =c->blockChain();
auto h = bc.currentHash();
auto blockData = bc.block(h);
BlockInfo info(blockData);
if (iss.peek() != -1)
{
string hexAddr;
u256 amount;
u256 gasPrice;
u256 gas;
string sechex;
string sdata;
u256 nonce;
iss >> hexAddr >> amount >> gasPrice >> gas >> sechex >> sdata >> nonce;
if (!gasPrice)
gasPrice = gasPricer->bid(priority);
cnote << "Data:";
cnote << sdata;
bytes data = dev::eth::parseData(sdata);
cnote << "Bytes:";
string sbd = asString(data);
bytes bbd = asBytes(sbd);
stringstream ssbd;
ssbd << bbd;
cnote << ssbd.str();
int ssize = sechex.length();
int size = hexAddr.length();
u256 minGas = (u256)Transaction::gasRequired(data, 0);
if (size < 40)
{
if (size > 0)
cwarn << "Invalid address length:" << size;
}
else if (gas < minGas)
cwarn << "Minimum gas amount is" << minGas;
else if (ssize < 40)
{
if (ssize > 0)
cwarn << "Invalid secret length:" << ssize;
}
else
{
try
{
Secret secret = h256(fromHex(sechex));
Address dest = h160(fromHex(hexAddr));
c->submitTransaction(secret, amount, dest, data, gas, gasPrice, nonce);
}
catch (BadHexCharacter& _e)
{
cwarn << "invalid hex character, transaction rejected";
cwarn << boost::diagnostic_information(_e);
}
catch (...)
{
cwarn << "transaction rejected";
}
}
}
else
cwarn << "Require parameters: submitTransaction ADDRESS AMOUNT GASPRICE GAS SECRET DATA NONCE";
}
else if (c && cmd == "txcreate") else if (c && cmd == "txcreate")
{ {
auto const& bc =c->blockChain(); auto const& bc =c->blockChain();
@ -1268,19 +1372,48 @@ int main(int argc, char** argv)
cout << "balance of " << stringHash << " is: " << toString(c->balanceAt(address)) << endl; cout << "balance of " << stringHash << " is: " << toString(c->balanceAt(address)) << endl;
} }
} }
// TODO implement << operator for std::unorderd_map else if (c && cmd == "balanceatblock")
// else if (c && cmd == "storageat") {
// { if (iss.peek() != -1)
// if (iss.peek() != -1) {
// { string stringHash;
// string stringHash; unsigned blocknumber;
// iss >> stringHash; iss >> stringHash >> blocknumber;
// Address address = h160(fromHex(stringHash)); Address address = h160(fromHex(stringHash));
// cout << "storage at " << stringHash << " is: " << c->storageAt(address) << endl; cout << "balance of " << stringHash << " is: " << toString(c->balanceAt(address, blocknumber)) << endl;
// } }
// } }
else if (c && cmd == "storageat")
{
if (iss.peek() != -1)
{
string stringHash;
iss >> stringHash;
Address address = h160(fromHex(stringHash));
cout << "storage at " << stringHash << " is: " << endl;
for (auto s: c->storageAt(address))
cout << toHex(s.first) << " : " << toHex(s.second) << endl;
}
}
else if (c && cmd == "storageatblock")
{
if (iss.peek() != -1)
{
string stringHash;
unsigned blocknumber;
iss >> stringHash >> blocknumber;
Address address = h160(fromHex(stringHash));
cout << "storage at " << stringHash << " is: " << endl;
for (auto s: c->storageAt(address, blocknumber))
cout << "\"0x" << toHex(s.first) << "\" : \"0x" << toHex(s.second) << "\"," << endl;
}
}
else if (c && cmd == "codeat") else if (c && cmd == "codeat")
{ {
if (iss.peek() != -1) if (iss.peek() != -1)
@ -1294,6 +1427,7 @@ int main(int argc, char** argv)
} }
} }
#endif #endif
else if (c && cmd == "send") else if (c && cmd == "send")
{ {
if (iss.peek() != -1) if (iss.peek() != -1)
@ -1403,6 +1537,22 @@ int main(int argc, char** argv)
cout << "Hex: " << toHex(rb) << endl; cout << "Hex: " << toHex(rb) << endl;
cout << r << endl; cout << r << endl;
} }
else if (c && cmd == "reprocess")
{
string block;
iss >> block;
h256 blockHash;
try
{
if (block.size() == 64 || block.size() == 66)
blockHash = h256(block);
else
blockHash = c->blockChain().numberHash(stoi(block));
c->state(blockHash);
}
catch (...)
{}
}
else if (c && cmd == "dumptrace") else if (c && cmd == "dumptrace")
{ {
unsigned block; unsigned block;
@ -1413,7 +1563,7 @@ int main(int argc, char** argv)
ofstream f; ofstream f;
f.open(filename); f.open(filename);
dev::eth::State state =c->state(index + 1,c->blockChain().numberHash(block)); dev::eth::State state = c->state(index + 1,c->blockChain().numberHash(block));
if (index < state.pending().size()) if (index < state.pending().size())
{ {
Executive e(state, c->blockChain(), 0); Executive e(state, c->blockChain(), 0);
@ -1605,7 +1755,7 @@ int main(int argc, char** argv)
while (!g_exit) while (!g_exit)
this_thread::sleep_for(chrono::milliseconds(1000)); this_thread::sleep_for(chrono::milliseconds(1000));
StructuredLogger::stopping(clientImplString, dev::Version); StructuredLogger::stopping(WebThreeDirect::composeClientVersion("++eth", clientName), dev::Version);
auto netData = web3.saveNetwork(); auto netData = web3.saveNetwork();
if (!netData.empty()) if (!netData.empty())
writeFile((dbPath.size() ? dbPath : getDataDir()) + "/network.rlp", netData); writeFile((dbPath.size() ? dbPath : getDataDir()) + "/network.rlp", netData);

120
ethminer/MinerAux.h

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

BIN
install-folder-bg.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
install-folder-bg@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

12
libdevcore/Base64.cpp

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

2
libdevcore/Common.cpp

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

9
libdevcore/FixedHash.h

@ -100,7 +100,7 @@ public:
FixedHash operator|(FixedHash const& _c) const { return FixedHash(*this) |= _c; } FixedHash operator|(FixedHash const& _c) const { return FixedHash(*this) |= _c; }
FixedHash& operator&=(FixedHash const& _c) { for (unsigned i = 0; i < N; ++i) m_data[i] &= _c.m_data[i]; return *this; } FixedHash& operator&=(FixedHash const& _c) { for (unsigned i = 0; i < N; ++i) m_data[i] &= _c.m_data[i]; return *this; }
FixedHash operator&(FixedHash const& _c) const { return FixedHash(*this) &= _c; } FixedHash operator&(FixedHash const& _c) const { return FixedHash(*this) &= _c; }
FixedHash& operator~() { for (unsigned i = 0; i < N; ++i) m_data[i] = ~m_data[i]; return *this; } FixedHash operator~() const { FixedHash ret; for (unsigned i = 0; i < N; ++i) ret[i] = ~m_data[i]; return ret; }
/// @returns true if all bytes in @a _c are set in this object. /// @returns true if all bytes in @a _c are set in this object.
bool contains(FixedHash const& _c) const { return (*this & _c) == _c; } bool contains(FixedHash const& _c) const { return (*this & _c) == _c; }
@ -158,16 +158,17 @@ public:
template <unsigned P, unsigned M> inline FixedHash& shiftBloom(FixedHash<M> const& _h) template <unsigned P, unsigned M> inline FixedHash& shiftBloom(FixedHash<M> const& _h)
{ {
return (*this |= _h.template bloom<P, N>()); return (*this |= _h.template bloomPart<P, N>());
} }
template <unsigned P, unsigned M> inline bool containsBloom(FixedHash<M> const& _h) template <unsigned P, unsigned M> inline bool containsBloom(FixedHash<M> const& _h)
{ {
return contains(_h.template bloom<P, N>()); return contains(_h.template bloomPart<P, N>());
} }
template <unsigned P, unsigned M> inline FixedHash<M> bloom() const template <unsigned P, unsigned M> inline FixedHash<M> bloomPart() const
{ {
static_assert((M & (M - 1)) == 0, "M must be power-of-two");
static const unsigned c_bloomBits = M * 8; static const unsigned c_bloomBits = M * 8;
unsigned mask = c_bloomBits - 1; unsigned mask = c_bloomBits - 1;
unsigned bloomBytes = (dev::toLog2(c_bloomBits) + 7) / 8; unsigned bloomBytes = (dev::toLog2(c_bloomBits) + 7) / 8;

74
libethash-cl/ethash_cl_miner.cpp

@ -32,11 +32,11 @@
#include <vector> #include <vector>
#include <libethash/util.h> #include <libethash/util.h>
#include <libethash/ethash.h> #include <libethash/ethash.h>
#include <libethash/internal.h>
#include "ethash_cl_miner.h" #include "ethash_cl_miner.h"
#include "ethash_cl_miner_kernel.h" #include "ethash_cl_miner_kernel.h"
#define ETHASH_BYTES 32 #define ETHASH_BYTES 32
#define ETHASH_CL_MINIMUM_MEMORY 2000000000
// workaround lame platforms // workaround lame platforms
#if !CL_VERSION_1_2 #if !CL_VERSION_1_2
@ -51,6 +51,8 @@ using namespace std;
// TODO: If at any point we can use libdevcore in here then we should switch to using a LogChannel // TODO: If at any point we can use libdevcore in here then we should switch to using a LogChannel
#define ETHCL_LOG(_contents) cout << "[OPENCL]:" << _contents << endl #define ETHCL_LOG(_contents) cout << "[OPENCL]:" << _contents << endl
// Types of OpenCL devices we are interested in
#define ETHCL_QUERIED_DEVICE_TYPES (CL_DEVICE_TYPE_GPU | CL_DEVICE_TYPE_ACCELERATOR)
static void addDefinition(string& _source, char const* _id, unsigned _value) static void addDefinition(string& _source, char const* _id, unsigned _value)
{ {
@ -82,9 +84,8 @@ string ethash_cl_miner::platform_info(unsigned _platformId, unsigned _deviceId)
} }
// get GPU device of the selected platform // get GPU device of the selected platform
vector<cl::Device> devices;
unsigned platform_num = min<unsigned>(_platformId, platforms.size() - 1); unsigned platform_num = min<unsigned>(_platformId, platforms.size() - 1);
platforms[platform_num].getDevices(CL_DEVICE_TYPE_ALL, &devices); vector<cl::Device> devices = getDevices(platforms, _platformId);
if (devices.empty()) if (devices.empty())
{ {
ETHCL_LOG("No OpenCL devices found."); ETHCL_LOG("No OpenCL devices found.");
@ -99,6 +100,17 @@ string ethash_cl_miner::platform_info(unsigned _platformId, unsigned _deviceId)
return "{ \"platform\": \"" + platforms[platform_num].getInfo<CL_PLATFORM_NAME>() + "\", \"device\": \"" + device.getInfo<CL_DEVICE_NAME>() + "\", \"version\": \"" + device_version + "\" }"; return "{ \"platform\": \"" + platforms[platform_num].getInfo<CL_PLATFORM_NAME>() + "\", \"device\": \"" + device.getInfo<CL_DEVICE_NAME>() + "\", \"version\": \"" + device_version + "\" }";
} }
std::vector<cl::Device> ethash_cl_miner::getDevices(std::vector<cl::Platform> const& _platforms, unsigned _platformId)
{
vector<cl::Device> devices;
unsigned platform_num = min<unsigned>(_platformId, _platforms.size() - 1);
_platforms[platform_num].getDevices(
s_allowCPU ? CL_DEVICE_TYPE_ALL : ETHCL_QUERIED_DEVICE_TYPES,
&devices
);
return devices;
}
unsigned ethash_cl_miner::getNumPlatforms() unsigned ethash_cl_miner::getNumPlatforms()
{ {
vector<cl::Platform> platforms; vector<cl::Platform> platforms;
@ -116,9 +128,7 @@ unsigned ethash_cl_miner::getNumDevices(unsigned _platformId)
return 0; return 0;
} }
vector<cl::Device> devices; vector<cl::Device> devices = getDevices(platforms, _platformId);
unsigned platform_num = min<unsigned>(_platformId, platforms.size() - 1);
platforms[platform_num].getDevices(CL_DEVICE_TYPE_ALL, &devices);
if (devices.empty()) if (devices.empty())
{ {
ETHCL_LOG("No OpenCL devices found."); ETHCL_LOG("No OpenCL devices found.");
@ -127,13 +137,24 @@ unsigned ethash_cl_miner::getNumDevices(unsigned _platformId)
return devices.size(); return devices.size();
} }
bool ethash_cl_miner::configureGPU() bool ethash_cl_miner::configureGPU(
bool _allowCPU,
unsigned _extraGPUMemory,
bool _forceSingleChunk,
boost::optional<uint64_t> _currentBlock
)
{ {
return searchForAllDevices([](cl::Device const _device) -> bool s_allowCPU = _allowCPU;
s_forceSingleChunk = _forceSingleChunk;
s_extraRequiredGPUMem = _extraGPUMemory;
// by default let's only consider the DAG of the first epoch
uint64_t dagSize = _currentBlock ? ethash_get_datasize(*_currentBlock) : 1073739904U;
uint64_t requiredSize = dagSize + _extraGPUMemory;
return searchForAllDevices([&requiredSize](cl::Device const _device) -> bool
{ {
cl_ulong result; cl_ulong result;
_device.getInfo(CL_DEVICE_GLOBAL_MEM_SIZE, &result); _device.getInfo(CL_DEVICE_GLOBAL_MEM_SIZE, &result);
if (result >= ETHASH_CL_MINIMUM_MEMORY) if (result >= requiredSize)
{ {
ETHCL_LOG( ETHCL_LOG(
"Found suitable OpenCL device [" << _device.getInfo<CL_DEVICE_NAME>() "Found suitable OpenCL device [" << _device.getInfo<CL_DEVICE_NAME>()
@ -145,13 +166,17 @@ bool ethash_cl_miner::configureGPU()
ETHCL_LOG( ETHCL_LOG(
"OpenCL device " << _device.getInfo<CL_DEVICE_NAME>() "OpenCL device " << _device.getInfo<CL_DEVICE_NAME>()
<< " has insufficient GPU memory." << result << << " has insufficient GPU memory." << result <<
" bytes of memory found < " << ETHASH_CL_MINIMUM_MEMORY << " bytes of memory required" " bytes of memory found < " << requiredSize << " bytes of memory required"
); );
return false; return false;
} }
); );
} }
bool ethash_cl_miner::s_allowCPU = false;
bool ethash_cl_miner::s_forceSingleChunk = false;
unsigned ethash_cl_miner::s_extraRequiredGPUMem;
bool ethash_cl_miner::searchForAllDevices(function<bool(cl::Device const&)> _callback) bool ethash_cl_miner::searchForAllDevices(function<bool(cl::Device const&)> _callback)
{ {
vector<cl::Platform> platforms; vector<cl::Platform> platforms;
@ -175,8 +200,7 @@ bool ethash_cl_miner::searchForAllDevices(unsigned _platformId, function<bool(cl
if (_platformId >= platforms.size()) if (_platformId >= platforms.size())
return false; return false;
vector<cl::Device> devices; vector<cl::Device> devices = getDevices(platforms, _platformId);
platforms[_platformId].getDevices(CL_DEVICE_TYPE_ALL, &devices);
for (cl::Device const& device: devices) for (cl::Device const& device: devices)
if (_callback(device)) if (_callback(device))
return true; return true;
@ -204,8 +228,7 @@ void ethash_cl_miner::doForAllDevices(unsigned _platformId, function<void(cl::De
if (_platformId >= platforms.size()) if (_platformId >= platforms.size())
return; return;
vector<cl::Device> devices; vector<cl::Device> devices = getDevices(platforms, _platformId);
platforms[_platformId].getDevices(CL_DEVICE_TYPE_ALL, &devices);
for (cl::Device const& device: devices) for (cl::Device const& device: devices)
_callback(device); _callback(device);
} }
@ -253,8 +276,7 @@ bool ethash_cl_miner::init(
ETHCL_LOG("Using platform: " << platforms[_platformId].getInfo<CL_PLATFORM_NAME>().c_str()); ETHCL_LOG("Using platform: " << platforms[_platformId].getInfo<CL_PLATFORM_NAME>().c_str());
// get GPU device of the default platform // get GPU device of the default platform
vector<cl::Device> devices; vector<cl::Device> devices = getDevices(platforms, _platformId);
platforms[_platformId].getDevices(CL_DEVICE_TYPE_ALL, &devices);
if (devices.empty()) if (devices.empty())
{ {
ETHCL_LOG("No OpenCL devices found."); ETHCL_LOG("No OpenCL devices found.");
@ -267,9 +289,22 @@ bool ethash_cl_miner::init(
ETHCL_LOG("Using device: " << device.getInfo<CL_DEVICE_NAME>().c_str() << "(" << device_version.c_str() << ")"); ETHCL_LOG("Using device: " << device.getInfo<CL_DEVICE_NAME>().c_str() << "(" << device_version.c_str() << ")");
// configure chunk number depending on max allocateable memory // configure chunk number depending on max allocateable memory
cl_ulong result; cl_ulong result;
device.getInfo(CL_DEVICE_MAX_MEM_ALLOC_SIZE, &result); device.getInfo(CL_DEVICE_MAX_MEM_ALLOC_SIZE, &result);
m_dagChunksNum = result >= ETHASH_CL_MINIMUM_MEMORY ? 4 : 1; if (s_forceSingleChunk || result >= _dagSize)
{
m_dagChunksNum = 1;
ETHCL_LOG(
((result <= _dagSize && s_forceSingleChunk) ? "Forcing single chunk. Good luck!\n" : "") <<
"Using 1 big chunk. Max OpenCL allocateable memory is " << result
);
}
else
{
m_dagChunksNum = 4;
ETHCL_LOG("Using 4 chunks. Max OpenCL allocateable memory is " << result);
}
if (strncmp("OpenCL 1.0", device_version.c_str(), 10) == 0) if (strncmp("OpenCL 1.0", device_version.c_str(), 10) == 0)
{ {
ETHCL_LOG("OpenCL 1.0 is not supported."); ETHCL_LOG("OpenCL 1.0 is not supported.");
@ -394,7 +429,8 @@ void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook
}; };
queue<pending_batch> pending; queue<pending_batch> pending;
static uint32_t const c_zero = 0; // this can't be a static because in MacOSX OpenCL implementation a segfault occurs when a static is passed to OpenCL functions
uint32_t const c_zero = 0;
// update header constant buffer // update header constant buffer
m_queue.enqueueWriteBuffer(m_header, false, 0, 32, header); m_queue.enqueueWriteBuffer(m_header, false, 0, 32, header);

18
libethash-cl/ethash_cl_miner.h

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

2
libethash-cl/ethash_cl_miner_kernel.cl

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

25
libethcore/Common.h

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

16
libethcore/Ethash.cpp

@ -108,7 +108,10 @@ bool Ethash::verify(BlockInfo const& _header)
bool pre = preVerify(_header); bool pre = preVerify(_header);
#if !ETH_DEBUG #if !ETH_DEBUG
if (!pre) if (!pre)
{
cwarn << "Fail on preVerify";
return false; return false;
}
#endif #endif
auto result = EthashAux::eval(_header); auto result = EthashAux::eval(_header);
@ -381,9 +384,18 @@ void Ethash::GPUMiner::listDevices()
return ethash_cl_miner::listDevices(); return ethash_cl_miner::listDevices();
} }
bool Ethash::GPUMiner::configureGPU() bool Ethash::GPUMiner::configureGPU(
unsigned _platformId,
unsigned _deviceId,
bool _allowCPU,
unsigned _extraGPUMemory,
bool _forceSingleChunk,
boost::optional<uint64_t> _currentBlock
)
{ {
return ethash_cl_miner::configureGPU(); s_platformId = _platformId;
s_deviceId = _deviceId;
return ethash_cl_miner::configureGPU(_allowCPU, _extraGPUMemory, _forceSingleChunk, _currentBlock);
} }
#endif #endif

16
libethcore/Ethash.h

@ -87,11 +87,8 @@ public:
static unsigned instances() { return s_numInstances > 0 ? s_numInstances : std::thread::hardware_concurrency(); } static unsigned instances() { return s_numInstances > 0 ? s_numInstances : std::thread::hardware_concurrency(); }
static std::string platformInfo(); static std::string platformInfo();
static void setDefaultPlatform(unsigned) {}
static void setDagChunks(unsigned) {}
static void setDefaultDevice(unsigned) {}
static void listDevices() {} static void listDevices() {}
static bool configureGPU() { return false; } static bool configureGPU(unsigned, unsigned, bool, unsigned, bool, boost::optional<uint64_t>) { return false; }
static void setNumInstances(unsigned _instances) { s_numInstances = std::min<unsigned>(_instances, std::thread::hardware_concurrency()); } static void setNumInstances(unsigned _instances) { s_numInstances = std::min<unsigned>(_instances, std::thread::hardware_concurrency()); }
protected: protected:
void kickOff() override void kickOff() override
@ -120,9 +117,14 @@ public:
static std::string platformInfo(); static std::string platformInfo();
static unsigned getNumDevices(); static unsigned getNumDevices();
static void listDevices(); static void listDevices();
static bool configureGPU(); static bool configureGPU(
static void setDefaultPlatform(unsigned _id) { s_platformId = _id; } unsigned _platformId,
static void setDefaultDevice(unsigned _id) { s_deviceId = _id; } unsigned _deviceId,
bool _allowCPU,
unsigned _extraGPUMemory,
bool _forceSingleChunk,
boost::optional<uint64_t> _currentBlock
);
static void setNumInstances(unsigned _instances) { s_numInstances = std::min<unsigned>(_instances, getNumDevices()); } static void setNumInstances(unsigned _instances) { s_numInstances = std::min<unsigned>(_instances, getNumDevices()); }
protected: protected:

5
libethcore/EthashAux.cpp

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

1
libethcore/EthashAux.h

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

10
libethereum/BlockChain.cpp

@ -360,7 +360,7 @@ pair<ImportResult, ImportRoute> BlockChain::attemptImport(bytes const& _block, O
{ {
try try
{ {
return make_pair(ImportResult::Success, import(verifyBlock(_block, m_onBad), _stateDB, _ir)); return make_pair(ImportResult::Success, import(verifyBlock(_block, m_onBad, _ir), _stateDB, _ir));
} }
catch (UnknownParent&) catch (UnknownParent&)
{ {
@ -1066,12 +1066,16 @@ bytes BlockChain::block(h256 const& _hash) const
return m_blocks[_hash]; return m_blocks[_hash];
} }
VerifiedBlockRef BlockChain::verifyBlock(bytes const& _block, function<void(Exception&)> const& _onBad) VerifiedBlockRef BlockChain::verifyBlock(bytes const& _block, function<void(Exception&)> const& _onBad, ImportRequirements::value _ir)
{ {
VerifiedBlockRef res; VerifiedBlockRef res;
try try
{ {
res.info.populate(_block, CheckEverything); Strictness strictness = Strictness::CheckEverything;
if (~_ir & ImportRequirements::ValidNonce)
strictness = Strictness::IgnoreNonce;
res.info.populate(_block, strictness);
res.info.verifyInternals(&_block); res.info.verifyInternals(&_block);
} }
catch (Exception& ex) catch (Exception& ex)

2
libethereum/BlockChain.h

@ -259,7 +259,7 @@ public:
void garbageCollect(bool _force = false); void garbageCollect(bool _force = false);
/// Verify block and prepare it for enactment /// Verify block and prepare it for enactment
static VerifiedBlockRef verifyBlock(bytes const& _block, std::function<void(Exception&)> const& _onBad = std::function<void(Exception&)>()); static VerifiedBlockRef verifyBlock(bytes const& _block, std::function<void(Exception&)> const& _onBad = std::function<void(Exception&)>(), ImportRequirements::value _ir = ImportRequirements::Default);
/// Change the function that is called with a bad block. /// Change the function that is called with a bad block.
template <class T> void setOnBad(T const& _t) { m_onBad = _t; } template <class T> void setOnBad(T const& _t) { m_onBad = _t; }

123
libethereum/Client.cpp

@ -611,16 +611,26 @@ bool Client::submitWork(ProofOfWork::Solution const& _solution)
return true; return true;
} }
unsigned static const c_syncMin = 1;
unsigned static const c_syncMax = 100;
double static const c_targetDuration = 1;
void Client::syncBlockQueue() void Client::syncBlockQueue()
{ {
ImportRoute ir; ImportRoute ir;
cwork << "BQ ==> CHAIN ==> STATE"; cwork << "BQ ==> CHAIN ==> STATE";
{ boost::timer t;
tie(ir.first, ir.second, m_syncBlockQueue) = m_bc.sync(m_bq, m_stateDB, rand() % 90 + 10); tie(ir.first, ir.second, m_syncBlockQueue) = m_bc.sync(m_bq, m_stateDB, m_syncAmount);
if (ir.first.empty()) double elapsed = t.elapsed();
return;
} cnote << m_syncAmount << "blocks imported in" << unsigned(elapsed * 1000) << "ms (" << (m_syncAmount / elapsed) << "blocks/s)";
if (elapsed > c_targetDuration * 1.1 && m_syncAmount > c_syncMin)
m_syncAmount = max(c_syncMin, m_syncAmount * 9 / 10);
else if (elapsed < c_targetDuration * 0.9 && m_syncAmount < c_syncMax)
m_syncAmount = min(c_syncMax, m_syncAmount * 11 / 10 + 1);
if (ir.first.empty())
return;
onChainChanged(ir); onChainChanged(ir);
} }
@ -691,43 +701,46 @@ void Client::onChainChanged(ImportRoute const& _ir)
// RESTART MINING // RESTART MINING
bool preChanged = false; if (!m_bq.items().first)
State newPreMine; {
DEV_READ_GUARDED(x_preMine) bool preChanged = false;
newPreMine = m_preMine; State newPreMine;
DEV_READ_GUARDED(x_preMine)
newPreMine = m_preMine;
// TODO: use m_postMine to avoid re-evaluating our own blocks. // TODO: use m_postMine to avoid re-evaluating our own blocks.
preChanged = newPreMine.sync(m_bc); preChanged = newPreMine.sync(m_bc);
if (preChanged || m_postMine.address() != m_preMine.address()) if (preChanged || m_postMine.address() != m_preMine.address())
{ {
if (isMining()) if (isMining())
cnote << "New block on chain."; cnote << "New block on chain.";
DEV_WRITE_GUARDED(x_preMine)
m_preMine = newPreMine;
DEV_WRITE_GUARDED(x_working)
m_working = newPreMine;
DEV_READ_GUARDED(x_postMine)
for (auto const& t: m_postMine.pending())
{
clog(ClientNote) << "Resubmitting post-mine transaction " << t;
auto ir = m_tq.import(t, TransactionQueue::ImportCallback(), IfDropped::Retry);
if (ir != ImportResult::Success)
onTransactionQueueReady();
}
DEV_READ_GUARDED(x_working) DEV_WRITE_GUARDED(x_postMine)
m_postMine = m_working;
DEV_WRITE_GUARDED(x_preMine) changeds.insert(PendingChangedFilter);
m_preMine = newPreMine;
DEV_WRITE_GUARDED(x_working)
m_working = newPreMine;
DEV_READ_GUARDED(x_postMine)
for (auto const& t: m_postMine.pending())
{
clog(ClientNote) << "Resubmitting post-mine transaction " << t;
auto ir = m_tq.import(t, TransactionQueue::ImportCallback(), IfDropped::Retry);
if (ir != ImportResult::Success)
onTransactionQueueReady();
}
DEV_READ_GUARDED(x_working) DEV_WRITE_GUARDED(x_postMine)
m_postMine = m_working;
changeds.insert(PendingChangedFilter); onPostStateChanged();
}
onPostStateChanged(); // Quick hack for now - the TQ at this point already has the prior pending transactions in it;
// we should resync with it manually until we are stricter about what constitutes "knowing".
onTransactionQueueReady();
} }
// Quick hack for now - the TQ at this point already has the prior pending transactions in it;
// we should resync with it manually until we are stricter about what constitutes "knowing".
onTransactionQueueReady();
noteChanged(changeds); noteChanged(changeds);
} }
@ -866,7 +879,9 @@ State Client::asOf(h256 const& _block) const
{ {
try try
{ {
return State(m_stateDB, bc(), _block); State ret(m_stateDB);
ret.populateFromChain(bc(), _block);
return ret;
} }
catch (Exception& ex) catch (Exception& ex)
{ {
@ -883,12 +898,36 @@ void Client::prepareForTransaction()
State Client::state(unsigned _txi, h256 _block) const State Client::state(unsigned _txi, h256 _block) const
{ {
return State(m_stateDB, m_bc, _block).fromPending(_txi); try
{
State ret(m_stateDB);
ret.populateFromChain(m_bc, _block);
return ret.fromPending(_txi);
}
catch (Exception& ex)
{
ex << errinfo_block(bc().block(_block));
onBadBlock(ex);
return State();
}
} }
eth::State Client::state(h256 _block) const State Client::state(h256 const& _block, PopulationStatistics* o_stats) const
{ {
return State(m_stateDB, m_bc, _block); try
{
State ret(m_stateDB);
PopulationStatistics s = ret.populateFromChain(m_bc, _block);
if (o_stats)
swap(s, *o_stats);
return ret;
}
catch (Exception& ex)
{
ex << errinfo_block(bc().block(_block));
onBadBlock(ex);
return State();
}
} }
eth::State Client::state(unsigned _txi) const eth::State Client::state(unsigned _txi) const
@ -904,8 +943,8 @@ void Client::flushTransactions()
doWork(); doWork();
} }
HashChainStatus Client::hashChainStatus() const SyncStatus Client::syncStatus() const
{ {
auto h = m_host.lock(); auto h = m_host.lock();
return h ? h->status() : HashChainStatus { 0, 0, false }; return h ? h->status() : SyncStatus();
} }

8
libethereum/Client.h

@ -147,7 +147,7 @@ public:
// [PRIVATE API - only relevant for base clients, not available in general] // [PRIVATE API - only relevant for base clients, not available in general]
dev::eth::State state(unsigned _txi, h256 _block) const; dev::eth::State state(unsigned _txi, h256 _block) const;
dev::eth::State state(h256 _block) const; dev::eth::State state(h256 const& _block, PopulationStatistics* o_stats = nullptr) const;
dev::eth::State state(unsigned _txi) const; dev::eth::State state(unsigned _txi) const;
/// Get the object representing the current state of Ethereum. /// Get the object representing the current state of Ethereum.
@ -157,7 +157,7 @@ public:
/// Get some information on the block queue. /// Get some information on the block queue.
BlockQueueStatus blockQueueStatus() const { return m_bq.status(); } BlockQueueStatus blockQueueStatus() const { return m_bq.status(); }
/// Get some information on the block queue. /// Get some information on the block queue.
HashChainStatus hashChainStatus() const; SyncStatus syncStatus() const;
/// Get the block queue. /// Get the block queue.
BlockQueue const& blockQueue() const { return m_bq; } BlockQueue const& blockQueue() const { return m_bq; }
@ -226,7 +226,7 @@ public:
/// Kills the blockchain. Just for debug use. /// Kills the blockchain. Just for debug use.
void killChain(); void killChain();
/// Retries all blocks with unknown parents. /// Retries all blocks with unknown parents.
void retryUnkonwn() { m_bq.retryAllUnknown(); } void retryUnknown() { m_bq.retryAllUnknown(); }
/// Get a report of activity. /// Get a report of activity.
ActivityReport activityReport() { ActivityReport ret; std::swap(m_report, ret); return ret; } ActivityReport activityReport() { ActivityReport ret; std::swap(m_report, ret); return ret; }
/// Set a JSONRPC server to which we can report bad blocks. /// Set a JSONRPC server to which we can report bad blocks.
@ -338,6 +338,8 @@ private:
mutable std::chrono::system_clock::time_point m_lastTick = std::chrono::system_clock::now(); mutable std::chrono::system_clock::time_point m_lastTick = std::chrono::system_clock::now();
///< When did we last tick()? ///< When did we last tick()?
unsigned m_syncAmount = 50; ///< Number of blocks to sync in each go.
ActivityReport m_report; ActivityReport m_report;
std::condition_variable m_signalled; std::condition_variable m_signalled;

18
libethereum/ClientBase.cpp

@ -45,19 +45,21 @@ State ClientBase::asOf(BlockNumber _h) const
return asOf(bc().numberHash(_h)); return asOf(bc().numberHash(_h));
} }
void ClientBase::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) void ClientBase::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, u256 _nonce)
{ {
prepareForTransaction(); prepareForTransaction();
auto a = toAddress(_secret); Transaction t(_value, _gasPrice, _gas, _dest, _data, _nonce, _secret);
u256 n = postMine().transactionsFrom(a);
cdebug << "submitTx: " << a << "postMine=" << n << "; tq=" << m_tq.maxNonce(a);
n = max<u256>(n, m_tq.maxNonce(a));
Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret);
m_tq.import(t.rlp()); m_tq.import(t.rlp());
StructuredLogger::transactionReceived(t.sha3().abridged(), t.sender().abridged()); StructuredLogger::transactionReceived(t.sha3().abridged(), t.sender().abridged());
cnote << "New transaction " << t << "(maxNonce for sender" << a << "is" << m_tq.maxNonce(a) << ")"; cnote << "New transaction " << t;
}
void ClientBase::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice)
{
auto a = toAddress(_secret);
submitTransaction(_secret, _value, _dest, _data, _gas, _gasPrice, max<u256>(postMine().transactionsFrom(a), m_tq.maxNonce(a)));
} }
Address ClientBase::submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice) Address ClientBase::submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice)

1
libethereum/ClientBase.h

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

25
libethereum/CommonNet.h

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

5
libethereum/DownloadMan.h

@ -253,11 +253,6 @@ public:
return m_got.full(); return m_got.full();
} }
unsigned gotCount() const
{
return m_got.size();
}
size_t chainSize() const { ReadGuard l(m_lock); return m_chainCount; } size_t chainSize() const { ReadGuard l(m_lock); return m_chainCount; }
size_t chainEmpty() const { ReadGuard l(m_lock); return m_chainCount == 0; } size_t chainEmpty() const { ReadGuard l(m_lock); return m_chainCount == 0; }
void foreachSub(std::function<void(HashDownloadSub const&)> const& _f) const { ReadGuard l(x_subs); for(auto i: m_subs) _f(*i); } void foreachSub(std::function<void(HashDownloadSub const&)> const& _f) const { ReadGuard l(x_subs); for(auto i: m_subs) _f(*i); }

137
libethereum/EthereumHost.cpp

@ -41,6 +41,8 @@ using namespace p2p;
unsigned const EthereumHost::c_oldProtocolVersion = 60; //TODO: remove this once v61+ is common unsigned const EthereumHost::c_oldProtocolVersion = 60; //TODO: remove this once v61+ is common
unsigned const c_chainReorgSize = 30000; unsigned const c_chainReorgSize = 30000;
char const* const EthereumHost::s_stateNames[static_cast<int>(SyncState::Size)] = {"Idle", "WaitingQueue", "HashesNegotiate", "HashesSingle", "HashesParallel", "Blocks", "NewBlocks" };
EthereumHost::EthereumHost(BlockChain const& _ch, TransactionQueue& _tq, BlockQueue& _bq, u256 _networkId): EthereumHost::EthereumHost(BlockChain const& _ch, TransactionQueue& _tq, BlockQueue& _bq, u256 _networkId):
HostCapability<EthereumPeer>(), HostCapability<EthereumPeer>(),
Worker ("ethsync"), Worker ("ethsync"),
@ -49,6 +51,7 @@ EthereumHost::EthereumHost(BlockChain const& _ch, TransactionQueue& _tq, BlockQu
m_bq (_bq), m_bq (_bq),
m_networkId (_networkId) m_networkId (_networkId)
{ {
setState(SyncState::HashesNegotiate);
m_latestBlockSent = _ch.currentHash(); m_latestBlockSent = _ch.currentHash();
m_hashMan.reset(m_chain.number() + 1); m_hashMan.reset(m_chain.number() + 1);
m_bqRoomAvailable = m_bq.onRoomAvailable([this](){ m_continueSync = true; }); m_bqRoomAvailable = m_bq.onRoomAvailable([this](){ m_continueSync = true; });
@ -79,8 +82,7 @@ void EthereumHost::reset()
foreachPeer([](EthereumPeer* _p) { _p->abortSync(); }); foreachPeer([](EthereumPeer* _p) { _p->abortSync(); });
m_man.resetToChain(h256s()); m_man.resetToChain(h256s());
m_hashMan.reset(m_chain.number() + 1); m_hashMan.reset(m_chain.number() + 1);
m_needSyncBlocks = true; setState(SyncState::HashesNegotiate);
m_needSyncHashes = true;
m_syncingLatestHash = h256(); m_syncingLatestHash = h256();
m_syncingTotalDifficulty = 0; m_syncingTotalDifficulty = 0;
m_latestBlockSent = h256(); m_latestBlockSent = h256();
@ -88,12 +90,28 @@ void EthereumHost::reset()
m_hashes.clear(); m_hashes.clear();
} }
void EthereumHost::resetSyncTo(h256 const& _h)
{
setState(SyncState::HashesNegotiate);
m_syncingLatestHash = _h;
}
void EthereumHost::setState(SyncState _s)
{
if (m_state != _s)
{
clog(NetAllDetail) << "SyncState changed from " << stateName(m_state) << " to " << stateName(_s);
m_state = _s;
}
}
void EthereumHost::doWork() void EthereumHost::doWork()
{ {
bool netChange = ensureInitialised(); bool netChange = ensureInitialised();
auto h = m_chain.currentHash(); auto h = m_chain.currentHash();
// If we've finished our initial sync (including getting all the blocks into the chain so as to reduce invalid transactions), start trading transactions & blocks // If we've finished our initial sync (including getting all the blocks into the chain so as to reduce invalid transactions), start trading transactions & blocks
if (isSyncing() && m_chain.isKnown(m_latestBlockSent)) if (!isSyncing() && m_chain.isKnown(m_latestBlockSent))
{ {
if (m_newTransactions) if (m_newTransactions)
{ {
@ -110,6 +128,7 @@ void EthereumHost::doWork()
if (m_continueSync) if (m_continueSync)
{ {
m_continueSync = false; m_continueSync = false;
RecursiveGuard l(x_sync);
continueSync(); continueSync();
} }
@ -152,6 +171,7 @@ void EthereumHost::maintainTransactions()
RLPStream ts; RLPStream ts;
_p->prep(ts, TransactionsPacket, n).appendRaw(b, n); _p->prep(ts, TransactionsPacket, n).appendRaw(b, n);
_p->sealAndSend(ts); _p->sealAndSend(ts);
cnote << "Sent" << n << "transactions to " << _p->session()->info().clientVersion;
} }
_p->m_requireTransactions = false; _p->m_requireTransactions = false;
}); });
@ -246,6 +266,7 @@ void EthereumHost::maintainBlocks(h256 const& _currentHash)
void EthereumHost::onPeerStatus(EthereumPeer* _peer) void EthereumHost::onPeerStatus(EthereumPeer* _peer)
{ {
RecursiveGuard l(x_sync); RecursiveGuard l(x_sync);
DEV_INVARIANT_CHECK;
if (_peer->m_genesisHash != m_chain.genesisHash()) if (_peer->m_genesisHash != m_chain.genesisHash())
_peer->disable("Invalid genesis hash"); _peer->disable("Invalid genesis hash");
else if (_peer->m_protocolVersion != protocolVersion() && _peer->m_protocolVersion != c_oldProtocolVersion) else if (_peer->m_protocolVersion != protocolVersion() && _peer->m_protocolVersion != c_oldProtocolVersion)
@ -265,13 +286,14 @@ void EthereumHost::onPeerStatus(EthereumPeer* _peer)
_peer->m_expectedHashes = (unsigned)_peer->m_latestBlockNumber - m_chain.number(); _peer->m_expectedHashes = (unsigned)_peer->m_latestBlockNumber - m_chain.number();
if (_peer->m_expectedHashes > estimatedHashes) if (_peer->m_expectedHashes > estimatedHashes)
_peer->disable("Too many hashes"); _peer->disable("Too many hashes");
else if (m_needSyncHashes && m_hashMan.chainSize() < _peer->m_expectedHashes) else if (needHashes() && m_hashMan.chainSize() < _peer->m_expectedHashes)
m_hashMan.resetToRange(m_chain.number() + 1, _peer->m_expectedHashes); m_hashMan.resetToRange(m_chain.number() + 1, _peer->m_expectedHashes);
} }
else else
_peer->m_expectedHashes = estimatedHashes; _peer->m_expectedHashes = estimatedHashes;
continueSync(_peer); continueSync(_peer);
} }
DEV_INVARIANT_CHECK;
} }
unsigned EthereumHost::estimateHashes() unsigned EthereumHost::estimateHashes()
@ -300,6 +322,7 @@ void EthereumHost::onPeerHashes(EthereumPeer* _peer, h256s const& _hashes)
void EthereumHost::onPeerHashes(EthereumPeer* _peer, h256s const& _hashes, bool _complete) void EthereumHost::onPeerHashes(EthereumPeer* _peer, h256s const& _hashes, bool _complete)
{ {
DEV_INVARIANT_CHECK;
if (_hashes.empty()) if (_hashes.empty())
{ {
_peer->m_hashSub.doneFetch(); _peer->m_hashSub.doneFetch();
@ -366,46 +389,52 @@ void EthereumHost::onPeerHashes(EthereumPeer* _peer, h256s const& _hashes, bool
} }
if (_complete) if (_complete)
{ {
m_needSyncBlocks = true; clog(NetMessageSummary) << "Start new blocks download...";
m_syncingLatestHash = h256();
setState(SyncState::NewBlocks);
m_man.resetToChain(m_hashes);
m_hashes.clear();
m_hashMan.reset(m_chain.number() + 1);
continueSync(_peer); continueSync(_peer);
} }
else if (syncByNumber && m_hashMan.isComplete()) else if (syncByNumber && m_hashMan.isComplete())
{ {
// Done our chain-get. // Done our chain-get.
m_needSyncHashes = false;
clog(NetNote) << "Hashes download complete."; clog(NetNote) << "Hashes download complete.";
// 1/100th for each useful block hash. onPeerDoneHashes(_peer, false);
_peer->addRating(m_man.chainSize() / 100); //TODO: what about other peers?
m_hashMan.reset(m_chain.number() + 1);
continueSync();
} }
else if (m_hashes.size() > _peer->m_expectedHashes) else if (m_hashes.size() > _peer->m_expectedHashes)
{ {
_peer->disable("Too many hashes"); _peer->disable("Too many hashes");
m_hashes.clear(); m_hashes.clear();
m_syncingLatestHash = h256(); m_syncingLatestHash = h256();
setState(SyncState::HashesNegotiate);
continueSync(); ///Try with some other peer, keep the chain continueSync(); ///Try with some other peer, keep the chain
} }
else else
continueSync(_peer); /// Grab next hashes continueSync(_peer); /// Grab next hashes
DEV_INVARIANT_CHECK;
} }
void EthereumHost::onPeerDoneHashes(EthereumPeer* _peer, bool _localChain) void EthereumHost::onPeerDoneHashes(EthereumPeer* _peer, bool _localChain)
{ {
assert(_peer->m_asking == Asking::Nothing); assert(_peer->m_asking == Asking::Nothing);
m_needSyncHashes = false; m_syncingLatestHash = h256();
setState(SyncState::Blocks);
if (_peer->m_protocolVersion != protocolVersion() || _localChain) if (_peer->m_protocolVersion != protocolVersion() || _localChain)
{ {
m_man.resetToChain(m_hashes); m_man.resetToChain(m_hashes);
m_hashes.clear(); _peer->addRating(m_man.chainSize() / 100); //TODO: what about other peers?
m_hashMan.reset(m_chain.number() + 1);
} }
m_hashMan.reset(m_chain.number() + 1);
m_hashes.clear();
continueSync(); continueSync();
} }
void EthereumHost::onPeerBlocks(EthereumPeer* _peer, RLP const& _r) void EthereumHost::onPeerBlocks(EthereumPeer* _peer, RLP const& _r)
{ {
RecursiveGuard l(x_sync); RecursiveGuard l(x_sync);
DEV_INVARIANT_CHECK;
_peer->setAsking(Asking::Nothing); _peer->setAsking(Asking::Nothing);
unsigned itemCount = _r.itemCount(); unsigned itemCount = _r.itemCount();
clog(NetMessageSummary) << "Blocks (" << dec << itemCount << "entries)" << (itemCount ? "" : ": NoMoreBlocks"); clog(NetMessageSummary) << "Blocks (" << dec << itemCount << "entries)" << (itemCount ? "" : ": NoMoreBlocks");
@ -425,6 +454,7 @@ void EthereumHost::onPeerBlocks(EthereumPeer* _peer, RLP const& _r)
unsigned unknown = 0; unsigned unknown = 0;
unsigned got = 0; unsigned got = 0;
unsigned repeated = 0; unsigned repeated = 0;
h256 lastUnknown;
for (unsigned i = 0; i < itemCount; ++i) for (unsigned i = 0; i < itemCount; ++i)
{ {
@ -453,6 +483,7 @@ void EthereumHost::onPeerBlocks(EthereumPeer* _peer, RLP const& _r)
break; break;
case ImportResult::UnknownParent: case ImportResult::UnknownParent:
lastUnknown = h;
unknown++; unknown++;
break; break;
@ -467,13 +498,22 @@ void EthereumHost::onPeerBlocks(EthereumPeer* _peer, RLP const& _r)
} }
clog(NetMessageSummary) << dec << success << "imported OK," << unknown << "with unknown parents," << future << "with future timestamps," << got << " already known," << repeated << " repeats received."; clog(NetMessageSummary) << dec << success << "imported OK," << unknown << "with unknown parents," << future << "with future timestamps," << got << " already known," << repeated << " repeats received.";
if (m_state == SyncState::NewBlocks && unknown > 0)
{
_peer->m_latestHash = lastUnknown;
resetSyncTo(lastUnknown);
}
continueSync(_peer); continueSync(_peer);
DEV_INVARIANT_CHECK;
} }
void EthereumHost::onPeerNewHashes(EthereumPeer* _peer, h256s const& _hashes) void EthereumHost::onPeerNewHashes(EthereumPeer* _peer, h256s const& _hashes)
{ {
RecursiveGuard l(x_sync); RecursiveGuard l(x_sync);
if (isSyncing_UNSAFE() || _peer->isConversing()) DEV_INVARIANT_CHECK;
if (isSyncing() || _peer->isConversing())
{ {
clog(NetMessageSummary) << "Ignoring new hashes since we're already downloading."; clog(NetMessageSummary) << "Ignoring new hashes since we're already downloading.";
return; return;
@ -481,12 +521,14 @@ void EthereumHost::onPeerNewHashes(EthereumPeer* _peer, h256s const& _hashes)
clog(NetNote) << "New block hash discovered: syncing without help."; clog(NetNote) << "New block hash discovered: syncing without help.";
_peer->m_syncHashNumber = 0; _peer->m_syncHashNumber = 0;
onPeerHashes(_peer, _hashes, true); onPeerHashes(_peer, _hashes, true);
DEV_INVARIANT_CHECK;
} }
void EthereumHost::onPeerNewBlock(EthereumPeer* _peer, RLP const& _r) void EthereumHost::onPeerNewBlock(EthereumPeer* _peer, RLP const& _r)
{ {
RecursiveGuard l(x_sync); RecursiveGuard l(x_sync);
if (isSyncing_UNSAFE() || _peer->isConversing()) DEV_INVARIANT_CHECK;
if ((isSyncing() || _peer->isConversing()) && m_state != SyncState::NewBlocks)
{ {
clog(NetMessageSummary) << "Ignoring new blocks since we're already downloading."; clog(NetMessageSummary) << "Ignoring new blocks since we're already downloading.";
return; return;
@ -526,9 +568,7 @@ void EthereumHost::onPeerNewBlock(EthereumPeer* _peer, RLP const& _r)
clog(NetMessageSummary) << "Received block with no known parent. Resyncing..."; clog(NetMessageSummary) << "Received block with no known parent. Resyncing...";
_peer->m_latestHash = h; _peer->m_latestHash = h;
_peer->m_totalDifficulty = difficulty; _peer->m_totalDifficulty = difficulty;
m_needSyncHashes = true; resetSyncTo(h);;
m_needSyncBlocks = true;
m_syncingLatestHash = h;
sync = true; sync = true;
} }
} }
@ -540,8 +580,9 @@ void EthereumHost::onPeerNewBlock(EthereumPeer* _peer, RLP const& _r)
_peer->m_knownBlocks.insert(h); _peer->m_knownBlocks.insert(h);
if (sync) if (sync)
continueSync(_peer); continueSync();
} }
DEV_INVARIANT_CHECK;
} }
void EthereumHost::onPeerTransactions(EthereumPeer* _peer, RLP const& _r) void EthereumHost::onPeerTransactions(EthereumPeer* _peer, RLP const& _r)
@ -578,7 +619,7 @@ void EthereumHost::onPeerAborting(EthereumPeer* _peer)
if (_peer->isConversing()) if (_peer->isConversing())
{ {
_peer->setIdle(); _peer->setIdle();
if (_peer->isCriticalSyncing()) // if (_peer->isCriticalSyncing())
_peer->setRude(); _peer->setRude();
continueSync(); continueSync();
} }
@ -586,6 +627,8 @@ void EthereumHost::onPeerAborting(EthereumPeer* _peer)
void EthereumHost::continueSync() void EthereumHost::continueSync()
{ {
if (m_state == SyncState::WaitingQueue)
setState(m_lastActiveState);
clog(NetAllDetail) << "Continuing sync for all peers"; clog(NetAllDetail) << "Continuing sync for all peers";
foreachPeer([&](EthereumPeer* _p) foreachPeer([&](EthereumPeer* _p)
{ {
@ -596,10 +639,11 @@ void EthereumHost::continueSync()
void EthereumHost::continueSync(EthereumPeer* _peer) void EthereumHost::continueSync(EthereumPeer* _peer)
{ {
DEV_INVARIANT_CHECK;
assert(_peer->m_asking == Asking::Nothing); assert(_peer->m_asking == Asking::Nothing);
bool otherPeerV60Sync = false; bool otherPeerV60Sync = false;
bool otherPeerV61Sync = false; bool otherPeerV61Sync = false;
if (m_needSyncHashes) if (needHashes())
{ {
if (!peerShouldGrabChain(_peer)) if (!peerShouldGrabChain(_peer))
{ {
@ -631,7 +675,7 @@ void EthereumHost::continueSync(EthereumPeer* _peer)
} }
if (_peer->m_protocolVersion == protocolVersion() && !m_hashMan.isComplete()) if (_peer->m_protocolVersion == protocolVersion() && !m_hashMan.isComplete())
{ {
m_syncingV61 = true; setState(SyncState::HashesParallel);
_peer->requestHashes(); /// v61+ and not catching up to a particular hash _peer->requestHashes(); /// v61+ and not catching up to a particular hash
} }
else else
@ -645,19 +689,19 @@ void EthereumHost::continueSync(EthereumPeer* _peer)
if (_peer->m_totalDifficulty >= m_syncingTotalDifficulty) if (_peer->m_totalDifficulty >= m_syncingTotalDifficulty)
{ {
_peer->requestHashes(m_syncingLatestHash); _peer->requestHashes(m_syncingLatestHash);
m_syncingV61 = false; setState(SyncState::HashesSingle);
m_estimatedHashes = _peer->m_expectedHashes; m_estimatedHashes = _peer->m_expectedHashes - (_peer->m_protocolVersion == protocolVersion() ? 0 : c_chainReorgSize);
} }
else else
_peer->setIdle(); _peer->setIdle();
} }
} }
else if (m_needSyncBlocks) else if (needBlocks())
{ {
if (m_man.isComplete()) if (m_man.isComplete())
{ {
// Done our chain-get. // Done our chain-get.
m_needSyncBlocks = false; setState(SyncState::Idle);
clog(NetNote) << "Chain download complete."; clog(NetNote) << "Chain download complete.";
// 1/100th for each useful block hash. // 1/100th for each useful block hash.
_peer->addRating(m_man.chainSize() / 100); //TODO: what about other peers? _peer->addRating(m_man.chainSize() / 100); //TODO: what about other peers?
@ -678,6 +722,8 @@ void EthereumHost::continueSync(EthereumPeer* _peer)
else if (m_bq.knownFull()) else if (m_bq.knownFull())
{ {
clog(NetAllDetail) << "Waiting for block queue before downloading blocks"; clog(NetAllDetail) << "Waiting for block queue before downloading blocks";
m_lastActiveState = m_state;
setState(SyncState::WaitingQueue);
_peer->setIdle(); _peer->setIdle();
} }
else else
@ -686,6 +732,7 @@ void EthereumHost::continueSync(EthereumPeer* _peer)
} }
else else
_peer->setIdle(); _peer->setIdle();
DEV_INVARIANT_CHECK;
} }
bool EthereumHost::peerCanHelp(EthereumPeer* _peer) const bool EthereumHost::peerCanHelp(EthereumPeer* _peer) const
@ -732,16 +779,42 @@ bool EthereumHost::peerShouldGrabChain(EthereumPeer* _peer) const
} }
} }
bool EthereumHost::isSyncing_UNSAFE() const bool EthereumHost::isSyncing() const
{ {
return m_needSyncBlocks || m_needSyncHashes; return m_state != SyncState::Idle;
} }
HashChainStatus EthereumHost::status() SyncStatus EthereumHost::status() const
{ {
RecursiveGuard l(x_sync); RecursiveGuard l(x_sync);
if (m_syncingV61) SyncStatus res;
return HashChainStatus { static_cast<unsigned>(m_hashMan.chainSize()), static_cast<unsigned>(m_hashMan.gotCount()), false }; res.state = m_state;
return HashChainStatus { m_estimatedHashes > 0 ? m_estimatedHashes - c_chainReorgSize : 0, static_cast<unsigned>(m_hashes.size()), m_estimatedHashes > 0 }; if (m_state == SyncState::HashesParallel)
{
res.hashesReceived = m_hashMan.hashesGot().size();
res.hashesTotal = m_hashMan.chainSize();
}
else if (m_state == SyncState::HashesSingle)
{
res.hashesTotal = m_estimatedHashes;
res.hashesReceived = static_cast<unsigned>(m_hashes.size());
res.hashesEstimated = true;
}
else if (m_state == SyncState::Blocks || m_state == SyncState::NewBlocks || m_state == SyncState::WaitingQueue)
{
res.blocksTotal = m_man.chainSize();
res.blocksReceived = m_man.blocksGot().size();
}
return res;
} }
bool EthereumHost::invariants() const
{
if (m_state == SyncState::HashesNegotiate && !m_hashes.empty())
return false;
if (needBlocks() && (m_syncingLatestHash || !m_hashes.empty()))
return false;
return true;
}

32
libethereum/EthereumHost.h

@ -54,9 +54,10 @@ class BlockQueue;
* @warning None of this is thread-safe. You have been warned. * @warning None of this is thread-safe. You have been warned.
* @doWork Syncs to peers and sends new blocks and transactions. * @doWork Syncs to peers and sends new blocks and transactions.
*/ */
class EthereumHost: public p2p::HostCapability<EthereumPeer>, Worker class EthereumHost: public p2p::HostCapability<EthereumPeer>, Worker, HasInvariants
{ {
public: public:
/// Start server, but don't listen. /// Start server, but don't listen.
EthereumHost(BlockChain const& _ch, TransactionQueue& _tq, BlockQueue& _bq, u256 _networkId); EthereumHost(BlockChain const& _ch, TransactionQueue& _tq, BlockQueue& _bq, u256 _networkId);
@ -70,7 +71,7 @@ public:
void reset(); void reset();
DownloadMan const& downloadMan() const { return m_man; } DownloadMan const& downloadMan() const { return m_man; }
bool isSyncing() const { RecursiveGuard l(x_sync); return isSyncing_UNSAFE(); } bool isSyncing() const;
bool isBanned(p2p::NodeId const& _id) const { return !!m_banned.count(_id); } bool isBanned(p2p::NodeId const& _id) const { return !!m_banned.count(_id); }
void noteNewTransactions() { m_newTransactions = true; } void noteNewTransactions() { m_newTransactions = true; }
@ -87,16 +88,21 @@ public:
DownloadMan& downloadMan() { return m_man; } DownloadMan& downloadMan() { return m_man; }
HashDownloadMan& hashDownloadMan() { return m_hashMan; } HashDownloadMan& hashDownloadMan() { return m_hashMan; }
BlockChain const& chain() { return m_chain; } BlockChain const& chain() { return m_chain; }
HashChainStatus status(); SyncStatus status() const;
static char const* stateName(SyncState _s) { return s_stateNames[static_cast<int>(_s)]; }
static unsigned const c_oldProtocolVersion; static unsigned const c_oldProtocolVersion;
private: private:
static char const* const s_stateNames[static_cast<int>(SyncState::Size)];
std::tuple<std::vector<std::shared_ptr<EthereumPeer>>, std::vector<std::shared_ptr<EthereumPeer>>, std::vector<std::shared_ptr<p2p::Session>>> randomSelection(unsigned _percent = 25, std::function<bool(EthereumPeer*)> const& _allow = [](EthereumPeer const*){ return true; }); std::tuple<std::vector<std::shared_ptr<EthereumPeer>>, std::vector<std::shared_ptr<EthereumPeer>>, std::vector<std::shared_ptr<p2p::Session>>> randomSelection(unsigned _percent = 25, std::function<bool(EthereumPeer*)> const& _allow = [](EthereumPeer const*){ return true; });
void foreachPeerPtr(std::function<void(std::shared_ptr<EthereumPeer>)> const& _f) const; void foreachPeerPtr(std::function<void(std::shared_ptr<EthereumPeer>)> const& _f) const;
void foreachPeer(std::function<void(EthereumPeer*)> const& _f) const; void foreachPeer(std::function<void(EthereumPeer*)> const& _f) const;
bool isSyncing_UNSAFE() const; void resetSyncTo(h256 const& _h);
bool needHashes() const { return m_state == SyncState::HashesNegotiate || m_state == SyncState::HashesSingle || m_state == SyncState::HashesParallel; }
bool needBlocks() const { return m_state == SyncState::Blocks || m_state == SyncState::NewBlocks; }
/// Sync with the BlockChain. It might contain one of our mined blocks, we might have new candidates from the network. /// Sync with the BlockChain. It might contain one of our mined blocks, we might have new candidates from the network.
void doWork(); void doWork();
@ -127,6 +133,9 @@ private:
bool peerCanHelp(EthereumPeer* _peer) const; bool peerCanHelp(EthereumPeer* _peer) const;
unsigned estimateHashes(); unsigned estimateHashes();
void estimatePeerHashes(EthereumPeer* _peer); void estimatePeerHashes(EthereumPeer* _peer);
void setState(SyncState _s);
bool invariants() const override;
BlockChain const& m_chain; BlockChain const& m_chain;
TransactionQueue& m_tq; ///< Maintains a list of incoming transactions not yet in a block on the blockchain. TransactionQueue& m_tq; ///< Maintains a list of incoming transactions not yet in a block on the blockchain.
@ -147,14 +156,13 @@ private:
bool m_newBlocks = false; bool m_newBlocks = false;
mutable RecursiveMutex x_sync; mutable RecursiveMutex x_sync;
bool m_needSyncHashes = true; ///< Indicates if need to downlad hashes SyncState m_state = SyncState::Idle; ///< Current sync state
bool m_needSyncBlocks = true; ///< Indicates if we still need to download some blocks SyncState m_lastActiveState = SyncState::Idle; ///< Saved state before entering waiting queue mode
h256 m_syncingLatestHash; ///< Latest block's hash, as of the current sync. h256 m_syncingLatestHash; ///< Latest block's hash, as of the current sync.
u256 m_syncingTotalDifficulty; ///< Latest block's total difficulty, as of the current sync. u256 m_syncingTotalDifficulty; ///< Latest block's total difficulty, as of the current sync.
h256s m_hashes; ///< List of hashes with unknown block numbers. Used for PV60 chain downloading and catching up to a particular unknown h256s m_hashes; ///< List of hashes with unknown block numbers. Used for PV60 chain downloading and catching up to a particular unknown
unsigned m_estimatedHashes = 0; ///< Number of estimated hashes for the last peer over PV60. Used for status reporting only. unsigned m_estimatedHashes = 0; ///< Number of estimated hashes for the last peer over PV60. Used for status reporting only.
bool m_syncingV61 = false; ///< True if recent activity was over pv61+. Used for status reporting only. bool m_continueSync = false; ///< True when the block queue has processed a block; we should restart grabbing blocks.
bool m_continueSync = false; ///< True when the block queue has processed a block; we should restart grabbing blocks.
}; };
} }

10
libethereum/EthereumPeer.cpp

@ -48,7 +48,6 @@ EthereumPeer::EthereumPeer(Session* _s, HostCapabilityFace* _h, unsigned _i, Cap
EthereumPeer::~EthereumPeer() EthereumPeer::~EthereumPeer()
{ {
clog(NetMessageSummary) << "Aborting Sync :-(";
abortSync(); abortSync();
} }
@ -57,8 +56,15 @@ bool EthereumPeer::isRude() const
return repMan().isRude(*session(), name()); return repMan().isRude(*session(), name());
} }
unsigned EthereumPeer::askOverride() const
{
bytes const& d = repMan().data(*session(), name());
return d.empty() ? c_maxBlocksAsk : RLP(d).toInt<unsigned>(RLP::LaisezFaire);
}
void EthereumPeer::setRude() void EthereumPeer::setRude()
{ {
repMan().setData(*session(), name(), rlp(askOverride() / 2 + 1));
repMan().noteRude(*session(), name()); repMan().noteRude(*session(), name());
session()->addNote("manners", "RUDE"); session()->addNote("manners", "RUDE");
} }
@ -140,7 +146,7 @@ void EthereumPeer::requestHashes(h256 const& _lastHash)
void EthereumPeer::requestBlocks() void EthereumPeer::requestBlocks()
{ {
setAsking(Asking::Blocks); setAsking(Asking::Blocks);
auto blocks = m_sub.nextFetch(isRude() ? 1 : c_maxBlocksAsk); auto blocks = m_sub.nextFetch(askOverride());
if (blocks.size()) if (blocks.size())
{ {
RLPStream s; RLPStream s;

3
libethereum/EthereumPeer.h

@ -91,6 +91,9 @@ public:
private: private:
using p2p::Capability::sealAndSend; using p2p::Capability::sealAndSend;
/// Figure out the amount of blocks we should be asking for.
unsigned askOverride() const;
/// Interpret an incoming message. /// Interpret an incoming message.
virtual bool interpret(unsigned _id, RLP const& _r); virtual bool interpret(unsigned _id, RLP const& _r);

2
libethereum/Executive.h

@ -53,6 +53,8 @@ public:
std::string json(bool _styled = false) const; std::string json(bool _styled = false) const;
OnOpFunc onOp() { return [&](uint64_t _steps, Instruction _inst, bigint _newMemSize, bigint _gasCost, bigint _gas, VM* _vm, ExtVMFace const* _extVM) { (*this)(_steps, _inst, _newMemSize, _gasCost, _gas, _vm, _extVM); }; }
private: private:
bool m_showMnemonics = false; bool m_showMnemonics = false;
std::vector<Instruction> m_lastInst; std::vector<Instruction> m_lastInst;

59
libethereum/ExtVM.cpp

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

25
libethereum/State.cpp

@ -115,21 +115,19 @@ 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, ImportRequirements::value _ir): PopulationStatistics State::populateFromChain(BlockChain const& _bc, h256 const& _h, ImportRequirements::value _ir)
m_db(_db),
m_state(&m_db),
m_blockReward(c_blockReward)
{ {
auto b = _bc.block(_h); PopulationStatistics ret;
BlockInfo bi(b);
if (!bi) if (!_bc.isKnown(_h))
{ {
// Might be worth throwing here. // Might be worth throwing here.
cwarn << "Invalid block given for state population: " << _h; cwarn << "Invalid block given for state population: " << _h;
return; return ret;
} }
auto b = _bc.block(_h);
BlockInfo bi(b);
if (bi.number) if (bi.number)
{ {
// Non-genesis: // Non-genesis:
@ -141,7 +139,12 @@ State::State(OverlayDB const& _db, BlockChain const& _bc, h256 _h, ImportRequire
// 2. Enact the block's transactions onto this state. // 2. Enact the block's transactions onto this state.
m_ourAddress = bi.coinbaseAddress; m_ourAddress = bi.coinbaseAddress;
enact(BlockChain::verifyBlock(b), _bc, _ir); boost::timer t;
auto vb = BlockChain::verifyBlock(b);
ret.verify = t.elapsed();
t.restart();
enact(vb, _bc, _ir);
ret.enact = t.elapsed();
} }
else else
{ {
@ -150,6 +153,8 @@ State::State(OverlayDB const& _db, BlockChain const& _bc, h256 _h, ImportRequire
m_state.init(); m_state.init();
sync(_bc, _h, bi, _ir); sync(_bc, _h, bi, _ir);
} }
return ret;
} }
State::State(State const& _s): State::State(State const& _s):
@ -595,7 +600,7 @@ string State::vmTrace(bytesConstRef _block, BlockChain const& _bc, ImportRequire
{ {
StandardTrace st; StandardTrace st;
st.setShowMnemonics(); st.setShowMnemonics();
execute(lh, Transaction(tr.data(), CheckTransaction::Everything), Permanence::Committed, [&](uint64_t _steps, Instruction _inst, bigint _newMemSize, bigint _gasCost, bigint _gas, VM* _vm, ExtVMFace const* _extVM) { st(_steps, _inst, _newMemSize, _gasCost, _gas, _vm, _extVM); }); execute(lh, Transaction(tr.data(), CheckTransaction::Everything), Permanence::Committed, st.onOp());
ret += (ret.empty() ? "[" : ",") + st.json(); ret += (ret.empty() ? "[" : ",") + st.json();
RLPStream receiptRLP; RLPStream receiptRLP;

12
libethereum/State.h

@ -123,6 +123,12 @@ enum class Permanence
Committed Committed
}; };
struct PopulationStatistics
{
double verify;
double enact;
};
/** /**
* @brief Model of the current state of the ledger. * @brief Model of the current state of the ledger.
* Maintains current ledger (m_current) as a fast hash-map. This is hashed only when required (i.e. to create or verify a block). * Maintains current ledger (m_current) as a fast hash-map. This is hashed only when required (i.e. to create or verify a block).
@ -146,9 +152,6 @@ public:
/// You can also set the coinbase address. /// You can also set the coinbase address.
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.
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);
@ -157,6 +160,9 @@ public:
~State(); ~State();
/// Construct state object from arbitrary point in blockchain.
PopulationStatistics populateFromChain(BlockChain const& _bc, h256 const& _hash, ImportRequirements::value _ir = ImportRequirements::Default);
/// Set the coinbase address for any transactions we do. /// Set the coinbase address for any transactions we do.
/// This causes a complete reset of current block. /// This causes a complete reset of current block.
void setAddress(Address _coinbaseAddress) { m_ourAddress = _coinbaseAddress; resetCurrent(); } void setAddress(Address _coinbaseAddress) { m_ourAddress = _coinbaseAddress; resetCurrent(); }

27
libevm/ExtVMFace.h

@ -63,12 +63,16 @@ using LogEntries = std::vector<LogEntry>;
struct LocalisedLogEntry: public LogEntry struct LocalisedLogEntry: public LogEntry
{ {
LocalisedLogEntry() {} LocalisedLogEntry() {}
explicit LocalisedLogEntry(LogEntry const& _le): LogEntry(_le) {}; explicit LocalisedLogEntry(LogEntry const& _le): LogEntry(_le) {}
explicit LocalisedLogEntry( explicit LocalisedLogEntry(
LogEntry const& _le, LogEntry const& _le,
h256 _special h256 _special
): LogEntry(_le), special(_special) {}; ):
LogEntry(_le),
isSpecial(true),
special(_special)
{}
explicit LocalisedLogEntry( explicit LocalisedLogEntry(
LogEntry const& _le, LogEntry const& _le,
@ -76,15 +80,24 @@ struct LocalisedLogEntry: public LogEntry
h256 _th, h256 _th,
unsigned _ti, unsigned _ti,
unsigned _li unsigned _li
): LogEntry(_le), blockHash(_bi.hash()), blockNumber((BlockNumber)_bi.number), transactionHash(_th), transactionIndex(_ti), logIndex(_li), mined(true) {}; ):
LogEntry(_le),
h256 blockHash = h256(); blockHash(_bi.hash()),
blockNumber((BlockNumber)_bi.number),
transactionHash(_th),
transactionIndex(_ti),
logIndex(_li),
mined(true)
{}
h256 blockHash;
BlockNumber blockNumber = 0; BlockNumber blockNumber = 0;
h256 transactionHash = h256(); h256 transactionHash;
unsigned transactionIndex = 0; unsigned transactionIndex = 0;
unsigned logIndex = 0; unsigned logIndex = 0;
bool mined = false; bool mined = false;
h256 special = h256(); bool isSpecial = false;
h256 special;
}; };
using LocalisedLogEntries = std::vector<LocalisedLogEntry>; using LocalisedLogEntries = std::vector<LocalisedLogEntry>;

48
libevmasm/CommonSubexpressionEliminator.cpp

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

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

File diff suppressed because one or more lines are too long

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

File diff suppressed because one or more lines are too long

27
libjsqrc/ethereumjs/dist/web3.js

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

2
libjsqrc/ethereumjs/gulpfile.js

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

13
libjsqrc/ethereumjs/lib/web3.js

@ -34,6 +34,7 @@ var Filter = require('./web3/filter');
var utils = require('./utils/utils'); var utils = require('./utils/utils');
var formatters = require('./web3/formatters'); var formatters = require('./web3/formatters');
var RequestManager = require('./web3/requestmanager'); var RequestManager = require('./web3/requestmanager');
var Method = require('./web3/method');
var c = require('./utils/config'); var c = require('./utils/config');
var Property = require('./web3/property'); var Property = require('./web3/property');
var Batch = require('./web3/batch'); var Batch = require('./web3/batch');
@ -158,5 +159,17 @@ setupProperties(web3.eth, eth.properties);
setupMethods(web3.db, db.methods); setupMethods(web3.db, db.methods);
setupMethods(web3.shh, shh.methods); setupMethods(web3.shh, shh.methods);
web3.admin = {};
web3.admin.setSessionKey = function(s) { web3.admin.sessionKey = s; };
var blockQueueStatus = new Property({
name: 'blockQueueStatus',
call: 'admin_eth_blockQueueStatus',
params: 1,
inputFormatter: [function() { return web3.admin.sessionKey; }]
});
setupMethods(web3.admin, [blockQueueStatus]);
module.exports = web3; module.exports = web3;

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

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

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

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

96
libp2p/Host.cpp

@ -61,17 +61,41 @@ ReputationManager::ReputationManager()
void ReputationManager::noteRude(Session const& _s, std::string const& _sub) void ReputationManager::noteRude(Session const& _s, std::string const& _sub)
{ {
m_nodes[make_pair(_s.id(), _s.info().clientVersion)].subs[_sub].isRude = true; DEV_WRITE_GUARDED(x_nodes)
m_nodes[make_pair(_s.id(), _s.info().clientVersion)].subs[_sub].isRude = true;
} }
bool ReputationManager::isRude(Session const& _s, std::string const& _sub) const bool ReputationManager::isRude(Session const& _s, std::string const& _sub) const
{ {
auto nit = m_nodes.find(make_pair(_s.id(), _s.info().clientVersion)); DEV_READ_GUARDED(x_nodes)
if (nit == m_nodes.end()) {
return false; auto nit = m_nodes.find(make_pair(_s.id(), _s.info().clientVersion));
auto sit = nit->second.subs.find(_sub); if (nit == m_nodes.end())
bool ret = sit == nit->second.subs.end() ? false : sit->second.isRude; return false;
return _sub.empty() ? ret : (ret || isRude(_s)); auto sit = nit->second.subs.find(_sub);
bool ret = sit == nit->second.subs.end() ? false : sit->second.isRude;
return _sub.empty() ? ret : (ret || isRude(_s));
}
return false;
}
void ReputationManager::setData(Session const& _s, std::string const& _sub, bytes const& _data)
{
DEV_WRITE_GUARDED(x_nodes)
m_nodes[make_pair(_s.id(), _s.info().clientVersion)].subs[_sub].data = _data;
}
bytes ReputationManager::data(Session const& _s, std::string const& _sub) const
{
DEV_READ_GUARDED(x_nodes)
{
auto nit = m_nodes.find(make_pair(_s.id(), _s.info().clientVersion));
if (nit == m_nodes.end())
return bytes();
auto sit = nit->second.subs.find(_sub);
return sit == nit->second.subs.end() ? bytes() : sit->second.data;
}
return bytes();
} }
Host::Host(std::string const& _clientVersion, NetworkPreferences const& _n, bytesConstRef _restoreNetwork): Host::Host(std::string const& _clientVersion, NetworkPreferences const& _n, bytesConstRef _restoreNetwork):
@ -191,7 +215,7 @@ void Host::doneWorking()
m_sessions.clear(); m_sessions.clear();
} }
void Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io, bi::tcp::endpoint _endpoint) void Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameCoder* _io, std::shared_ptr<RLPXSocket> const& _s)
{ {
// session maybe ingress or egress so m_peers and node table entries may not exist // session maybe ingress or egress so m_peers and node table entries may not exist
shared_ptr<Peer> p; shared_ptr<Peer> p;
@ -211,7 +235,7 @@ void Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io
} }
if (p->isOffline()) if (p->isOffline())
p->m_lastConnected = std::chrono::system_clock::now(); p->m_lastConnected = std::chrono::system_clock::now();
p->endpoint.address = _endpoint.address(); p->endpoint.address = _s->remoteEndpoint().address();
auto protocolVersion = _rlp[0].toInt<unsigned>(); auto protocolVersion = _rlp[0].toInt<unsigned>();
auto clientVersion = _rlp[1].toString(); auto clientVersion = _rlp[1].toString();
@ -230,7 +254,7 @@ void Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io
clog(NetMessageSummary) << "Hello: " << clientVersion << "V[" << protocolVersion << "]" << _id << showbase << capslog.str() << dec << listenPort; clog(NetMessageSummary) << "Hello: " << clientVersion << "V[" << protocolVersion << "]" << _id << showbase << capslog.str() << dec << listenPort;
// create session so disconnects are managed // create session so disconnects are managed
auto ps = make_shared<Session>(this, _io, p, PeerSessionInfo({_id, clientVersion, _endpoint.address().to_string(), listenPort, chrono::steady_clock::duration(), _rlp[2].toSet<CapDesc>(), 0, map<string, string>()})); auto ps = make_shared<Session>(this, _io, _s, p, PeerSessionInfo({_id, clientVersion, p->endpoint.address.to_string(), listenPort, chrono::steady_clock::duration(), _rlp[2].toSet<CapDesc>(), 0, map<string, string>()}));
if (protocolVersion < dev::p2p::c_protocolVersion - 1) if (protocolVersion < dev::p2p::c_protocolVersion - 1)
{ {
ps->disconnect(IncompatibleProtocol); ps->disconnect(IncompatibleProtocol);
@ -302,7 +326,8 @@ void Host::onNodeTableEvent(NodeId const& _n, NodeTableEventType const& _e)
{ {
clog(NetP2PNote) << "p2p.host.nodeTable.events.NodeEntryDropped " << _n; clog(NetP2PNote) << "p2p.host.nodeTable.events.NodeEntryDropped " << _n;
RecursiveGuard l(x_sessions); RecursiveGuard l(x_sessions);
m_peers.erase(_n); if (m_peers.count(_n) && !m_peers[_n]->required)
m_peers.erase(_n);
} }
} }
@ -613,27 +638,40 @@ void Host::run(boost::system::error_code const&)
// updated. // disconnectLatePeers(); // updated. // disconnectLatePeers();
// todo: update peerSlotsAvailable() // todo: update peerSlotsAvailable()
unsigned pendingCount = 0;
DEV_GUARDED(x_pendingNodeConns) list<shared_ptr<Peer>> toConnect;
pendingCount = m_pendingPeerConns.size(); unsigned reqConn = 0;
int openSlots = m_idealPeerCount - peerCount() - pendingCount;
if (openSlots > 0)
{ {
list<shared_ptr<Peer>> toConnect; RecursiveGuard l(x_sessions);
for (auto const& p: m_peers)
{ {
RecursiveGuard l(x_sessions); bool haveSession = havePeerSession(p.second->id);
for (auto p: m_peers) bool required = p.second->required;
if (p.second->shouldReconnect() && !havePeerSession(p.second->id)) if (haveSession && required)
toConnect.push_back(p.second); reqConn++;
else if (!haveSession && p.second->shouldReconnect() && (!m_netPrefs.pin || required))
toConnect.push_back(p.second);
} }
}
for (auto p: toConnect)
if (p->required && reqConn++ < m_idealPeerCount)
connect(p);
if (!m_netPrefs.pin)
{
unsigned pendingCount = 0;
DEV_GUARDED(x_pendingNodeConns)
pendingCount = m_pendingPeerConns.size();
int openSlots = m_idealPeerCount - peerCount() - pendingCount + reqConn;
if (openSlots > 0)
{
for (auto p: toConnect)
if (!p->required && openSlots--)
connect(p);
for (auto p: toConnect) m_nodeTable->discover();
if (openSlots--) }
connect(p);
else
break;
m_nodeTable->discover();
} }
auto runcb = [this](boost::system::error_code const& error) { run(error); }; auto runcb = [this](boost::system::error_code const& error) { run(error); };
@ -674,7 +712,7 @@ void Host::startedWorking()
else else
clog(NetP2PNote) << "p2p.start.notice id:" << id() << "TCP Listen port is invalid or unavailable."; clog(NetP2PNote) << "p2p.start.notice id:" << id() << "TCP Listen port is invalid or unavailable.";
shared_ptr<NodeTable> nodeTable(new NodeTable(m_ioService, m_alias, NodeIPEndpoint(bi::address::from_string(listenAddress()), listenPort(), listenPort()))); shared_ptr<NodeTable> nodeTable(new NodeTable(m_ioService, m_alias, NodeIPEndpoint(bi::address::from_string(listenAddress()), listenPort(), listenPort()), m_netPrefs.discovery));
nodeTable->setEventHandler(new HostNodeTableHandler(*this)); nodeTable->setEventHandler(new HostNodeTableHandler(*this));
m_nodeTable = nodeTable; m_nodeTable = nodeTable;
restoreNetwork(&m_restoreNetwork); restoreNetwork(&m_restoreNetwork);

9
libp2p/Host.h

@ -40,7 +40,8 @@
#include "HostCapability.h" #include "HostCapability.h"
#include "Network.h" #include "Network.h"
#include "Peer.h" #include "Peer.h"
#include "RLPxFrameIO.h" #include "RLPXSocket.h"
#include "RLPXFrameCoder.h"
#include "Common.h" #include "Common.h"
namespace ba = boost::asio; namespace ba = boost::asio;
namespace bi = ba::ip; namespace bi = ba::ip;
@ -82,6 +83,7 @@ struct SubReputation
{ {
bool isRude = false; bool isRude = false;
int utility = 0; int utility = 0;
bytes data;
}; };
struct Reputation struct Reputation
@ -96,9 +98,12 @@ public:
void noteRude(Session const& _s, std::string const& _sub = std::string()); void noteRude(Session const& _s, std::string const& _sub = std::string());
bool isRude(Session const& _s, std::string const& _sub = std::string()) const; bool isRude(Session const& _s, std::string const& _sub = std::string()) const;
void setData(Session const& _s, std::string const& _sub, bytes const& _data);
bytes data(Session const& _s, std::string const& _subs) const;
private: private:
std::unordered_map<std::pair<p2p::NodeId, std::string>, Reputation> m_nodes; ///< Nodes that were impolite while syncing. We avoid syncing from these if possible. std::unordered_map<std::pair<p2p::NodeId, std::string>, Reputation> m_nodes; ///< Nodes that were impolite while syncing. We avoid syncing from these if possible.
SharedMutex mutable x_nodes;
}; };
/** /**
@ -196,7 +201,7 @@ public:
NodeId id() const { return m_alias.pub(); } NodeId id() const { return m_alias.pub(); }
/// Validates and starts peer session, taking ownership of _io. Disconnects and returns false upon error. /// Validates and starts peer session, taking ownership of _io. Disconnects and returns false upon error.
void startPeerSession(Public const& _id, RLP const& _hello, RLPXFrameIO* _io, bi::tcp::endpoint _endpoint); void startPeerSession(Public const& _id, RLP const& _hello, RLPXFrameCoder* _io, std::shared_ptr<RLPXSocket> const& _s);
protected: protected:
void onNodeTableEvent(NodeId const& _n, NodeTableEventType const& _e); void onNodeTableEvent(NodeId const& _n, NodeTableEventType const& _e);

2
libp2p/Network.h

@ -52,6 +52,8 @@ struct NetworkPreferences
std::string listenIPAddress; std::string listenIPAddress;
unsigned short listenPort = 30303; unsigned short listenPort = 30303;
bool traverseNAT = true; bool traverseNAT = true;
bool discovery = true; // Discovery is activated with network.
bool pin = false; // Only connect to trusted ("required") peers.
}; };
/** /**

12
libp2p/NodeTable.cpp

@ -40,14 +40,15 @@ const char* NodeTableIngress::name() { return "<<P"; }
NodeEntry::NodeEntry(NodeId const& _src, Public const& _pubk, NodeIPEndpoint const& _gw): Node(_pubk, _gw), distance(NodeTable::distance(_src, _pubk)) {} NodeEntry::NodeEntry(NodeId const& _src, Public const& _pubk, NodeIPEndpoint const& _gw): Node(_pubk, _gw), distance(NodeTable::distance(_src, _pubk)) {}
NodeTable::NodeTable(ba::io_service& _io, KeyPair const& _alias, NodeIPEndpoint const& _endpoint): NodeTable::NodeTable(ba::io_service& _io, KeyPair const& _alias, NodeIPEndpoint const& _endpoint, bool _enabled):
m_node(Node(_alias.pub(), _endpoint)), m_node(Node(_alias.pub(), _endpoint)),
m_secret(_alias.sec()), m_secret(_alias.sec()),
m_io(_io), m_io(_io),
m_socket(new NodeSocket(m_io, *this, (bi::udp::endpoint)m_node.endpoint)), m_socket(new NodeSocket(m_io, *this, (bi::udp::endpoint)m_node.endpoint)),
m_socketPointer(m_socket.get()), m_socketPointer(m_socket.get()),
m_bucketRefreshTimer(m_io), m_bucketRefreshTimer(m_io),
m_evictionCheckTimer(m_io) m_evictionCheckTimer(m_io),
m_disabled(!_enabled)
{ {
for (unsigned i = 0; i < s_bins; i++) for (unsigned i = 0; i < s_bins; i++)
{ {
@ -55,8 +56,11 @@ NodeTable::NodeTable(ba::io_service& _io, KeyPair const& _alias, NodeIPEndpoint
m_state[i].modified = chrono::steady_clock::now() - chrono::seconds(1); m_state[i].modified = chrono::steady_clock::now() - chrono::seconds(1);
} }
m_socketPointer->connect(); if (!m_disabled)
doRefreshBuckets(boost::system::error_code()); {
m_socketPointer->connect();
doRefreshBuckets(boost::system::error_code());
}
} }
NodeTable::~NodeTable() NodeTable::~NodeTable()

4
libp2p/NodeTable.h

@ -130,7 +130,7 @@ public:
enum NodeRelation { Unknown = 0, Known }; enum NodeRelation { Unknown = 0, Known };
/// Constructor requiring host for I/O, credentials, and IP Address and port to listen on. /// Constructor requiring host for I/O, credentials, and IP Address and port to listen on.
NodeTable(ba::io_service& _io, KeyPair const& _alias, NodeIPEndpoint const& _endpoint); NodeTable(ba::io_service& _io, KeyPair const& _alias, NodeIPEndpoint const& _endpoint, bool _enabled = true);
~NodeTable(); ~NodeTable();
/// Returns distance based on xor metric two node ids. Used by NodeEntry and NodeTable. /// Returns distance based on xor metric two node ids. Used by NodeEntry and NodeTable.
@ -271,6 +271,8 @@ private:
boost::asio::deadline_timer m_bucketRefreshTimer; ///< Timer which schedules and enacts bucket refresh. boost::asio::deadline_timer m_bucketRefreshTimer; ///< Timer which schedules and enacts bucket refresh.
boost::asio::deadline_timer m_evictionCheckTimer; ///< Timer for handling node evictions. boost::asio::deadline_timer m_evictionCheckTimer; ///< Timer for handling node evictions.
bool m_disabled; ///< Disable discovery.
}; };
inline std::ostream& operator<<(std::ostream& _out, NodeTable const& _nodeTable) inline std::ostream& operator<<(std::ostream& _out, NodeTable const& _nodeTable)

2
libp2p/Peer.cpp

@ -38,6 +38,8 @@ bool Peer::shouldReconnect() const
unsigned Peer::fallbackSeconds() const unsigned Peer::fallbackSeconds() const
{ {
if (required)
return 5;
switch (m_lastDisconnect) switch (m_lastDisconnect)
{ {
case BadProtocol: case BadProtocol:

30
libp2p/RLPxFrameIO.cpp → libp2p/RLPXFrameCoder.cpp

@ -14,16 +14,14 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file RLPXFrameIO.cpp /** @file RLPXFrameCoder.cpp
* @author Alex Leverington <nessence@gmail.com> * @author Alex Leverington <nessence@gmail.com>
* @date 2015 * @date 2015
*/ */
#include "RLPxFrameIO.h" #include "RLPXFrameCoder.h"
#include <libdevcore/Assertions.h> #include <libdevcore/Assertions.h>
#include "Host.h"
#include "Session.h"
#include "Peer.h"
#include "RLPxHandshake.h" #include "RLPxHandshake.h"
using namespace std; using namespace std;
@ -31,7 +29,7 @@ using namespace dev;
using namespace dev::p2p; using namespace dev::p2p;
using namespace CryptoPP; using namespace CryptoPP;
RLPXFrameIO::RLPXFrameIO(RLPXHandshake const& _init): m_socket(_init.m_socket) RLPXFrameCoder::RLPXFrameCoder(RLPXHandshake const& _init)
{ {
// we need: // we need:
// originated? // originated?
@ -94,7 +92,7 @@ RLPXFrameIO::RLPXFrameIO(RLPXHandshake const& _init): m_socket(_init.m_socket)
m_ingressMac.Update(keyMaterial.data(), keyMaterial.size()); m_ingressMac.Update(keyMaterial.data(), keyMaterial.size());
} }
void RLPXFrameIO::writeSingleFramePacket(bytesConstRef _packet, bytes& o_bytes) void RLPXFrameCoder::writeSingleFramePacket(bytesConstRef _packet, bytes& o_bytes)
{ {
// _packet = type || rlpList() // _packet = type || rlpList()
@ -126,7 +124,7 @@ void RLPXFrameIO::writeSingleFramePacket(bytesConstRef _packet, bytes& o_bytes)
egressDigest().ref().copyTo(macRef); egressDigest().ref().copyTo(macRef);
} }
bool RLPXFrameIO::authAndDecryptHeader(bytesRef io) bool RLPXFrameCoder::authAndDecryptHeader(bytesRef io)
{ {
asserts(io.size() == h256::size); asserts(io.size() == h256::size);
updateIngressMACWithHeader(io); updateIngressMACWithHeader(io);
@ -138,7 +136,7 @@ bool RLPXFrameIO::authAndDecryptHeader(bytesRef io)
return true; return true;
} }
bool RLPXFrameIO::authAndDecryptFrame(bytesRef io) bool RLPXFrameCoder::authAndDecryptFrame(bytesRef io)
{ {
bytesRef cipherText(io.cropped(0, io.size() - h128::size)); bytesRef cipherText(io.cropped(0, io.size() - h128::size));
updateIngressMACWithFrame(cipherText); updateIngressMACWithFrame(cipherText);
@ -149,7 +147,7 @@ bool RLPXFrameIO::authAndDecryptFrame(bytesRef io)
return true; return true;
} }
h128 RLPXFrameIO::egressDigest() h128 RLPXFrameCoder::egressDigest()
{ {
SHA3_256 h(m_egressMac); SHA3_256 h(m_egressMac);
h128 digest; h128 digest;
@ -157,7 +155,7 @@ h128 RLPXFrameIO::egressDigest()
return digest; return digest;
} }
h128 RLPXFrameIO::ingressDigest() h128 RLPXFrameCoder::ingressDigest()
{ {
SHA3_256 h(m_ingressMac); SHA3_256 h(m_ingressMac);
h128 digest; h128 digest;
@ -165,29 +163,29 @@ h128 RLPXFrameIO::ingressDigest()
return digest; return digest;
} }
void RLPXFrameIO::updateEgressMACWithHeader(bytesConstRef _headerCipher) void RLPXFrameCoder::updateEgressMACWithHeader(bytesConstRef _headerCipher)
{ {
updateMAC(m_egressMac, _headerCipher.cropped(0, 16)); updateMAC(m_egressMac, _headerCipher.cropped(0, 16));
} }
void RLPXFrameIO::updateEgressMACWithFrame(bytesConstRef _cipher) void RLPXFrameCoder::updateEgressMACWithFrame(bytesConstRef _cipher)
{ {
m_egressMac.Update(_cipher.data(), _cipher.size()); m_egressMac.Update(_cipher.data(), _cipher.size());
updateMAC(m_egressMac); updateMAC(m_egressMac);
} }
void RLPXFrameIO::updateIngressMACWithHeader(bytesConstRef _headerCipher) void RLPXFrameCoder::updateIngressMACWithHeader(bytesConstRef _headerCipher)
{ {
updateMAC(m_ingressMac, _headerCipher.cropped(0, 16)); updateMAC(m_ingressMac, _headerCipher.cropped(0, 16));
} }
void RLPXFrameIO::updateIngressMACWithFrame(bytesConstRef _cipher) void RLPXFrameCoder::updateIngressMACWithFrame(bytesConstRef _cipher)
{ {
m_ingressMac.Update(_cipher.data(), _cipher.size()); m_ingressMac.Update(_cipher.data(), _cipher.size());
updateMAC(m_ingressMac); updateMAC(m_ingressMac);
} }
void RLPXFrameIO::updateMAC(SHA3_256& _mac, bytesConstRef _seed) void RLPXFrameCoder::updateMAC(SHA3_256& _mac, bytesConstRef _seed)
{ {
if (_seed.size() && _seed.size() != h128::size) if (_seed.size() && _seed.size() != h128::size)
asserts(false); asserts(false);

51
libp2p/RLPxFrameIO.h → libp2p/RLPXFrameCoder.h

@ -14,7 +14,7 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file RLPXFrameIO.h /** @file RLPXFrameCoder.h
* @author Alex Leverington <nessence@gmail.com> * @author Alex Leverington <nessence@gmail.com>
* @date 2015 * @date 2015
*/ */
@ -23,13 +23,10 @@
#pragma once #pragma once
#include <memory> #include <memory>
#include <libdevcrypto/Common.h> #include <libdevcore/Guards.h>
#include <libdevcrypto/ECDHE.h> #include <libdevcrypto/ECDHE.h>
#include <libdevcrypto/CryptoPP.h> #include <libdevcrypto/CryptoPP.h>
#include <libdevcore/Guards.h>
#include "Common.h" #include "Common.h"
namespace ba = boost::asio;
namespace bi = boost::asio::ip;
namespace dev namespace dev
{ {
@ -39,45 +36,21 @@ namespace p2p
class RLPXHandshake; class RLPXHandshake;
/** /**
* @brief Encoder/decoder transport for RLPx connections established by RLPXHandshake. * @brief Encoder/decoder transport for RLPx connection established by RLPXHandshake.
* Managed (via shared_ptr) socket for use by RLPXHandshake and RLPXFrameIO.
*
* Thread Safety
* Distinct Objects: Safe.
* Shared objects: Unsafe.
* * an instance method must not be called concurrently
* * a writeSingleFramePacket can be called concurrent to authAndDecryptHeader OR authAndDecryptFrame
*/
class RLPXSocket: public std::enable_shared_from_this<RLPXSocket>
{
public:
RLPXSocket(bi::tcp::socket* _socket): m_socket(std::move(*_socket)) {}
~RLPXSocket() { close(); }
bool isConnected() const { return m_socket.is_open(); }
void close() { try { boost::system::error_code ec; m_socket.shutdown(bi::tcp::socket::shutdown_both, ec); if (m_socket.is_open()) m_socket.close(); } catch (...){} }
bi::tcp::endpoint remoteEndpoint() { boost::system::error_code ec; return m_socket.remote_endpoint(ec); }
bi::tcp::socket& ref() { return m_socket; }
protected:
bi::tcp::socket m_socket;
};
/**
* @brief Encoder/decoder transport for RLPx connections established by RLPXHandshake.
* *
* Thread Safety * Thread Safety
* Distinct Objects: Safe. * Distinct Objects: Safe.
* Shared objects: Unsafe. * Shared objects: Unsafe.
*/ */
class RLPXFrameIO class RLPXFrameCoder
{ {
friend class RLPXFrameIOMux;
friend class Session; friend class Session;
public: public:
/// Constructor. /// Constructor.
/// Requires instance of RLPXHandshake which has completed first two phases of handshake. /// Requires instance of RLPXHandshake which has completed first two phases of handshake.
RLPXFrameIO(RLPXHandshake const& _init); RLPXFrameCoder(RLPXHandshake const& _init);
~RLPXFrameIO() {} ~RLPXFrameCoder() {}
/// Encrypt _packet as RLPx frame. /// Encrypt _packet as RLPx frame.
void writeSingleFramePacket(bytesConstRef _packet, bytes& o_bytes); void writeSingleFramePacket(bytesConstRef _packet, bytes& o_bytes);
@ -93,7 +66,7 @@ public:
/// Return first 16 bytes of current digest from ingress mac. /// Return first 16 bytes of current digest from ingress mac.
h128 ingressDigest(); h128 ingressDigest();
protected: protected:
/// Update state of egress MAC with frame header. /// Update state of egress MAC with frame header.
void updateEgressMACWithHeader(bytesConstRef _headerCipher); void updateEgressMACWithHeader(bytesConstRef _headerCipher);
@ -106,9 +79,7 @@ protected:
/// Update state of ingress MAC with frame. /// Update state of ingress MAC with frame.
void updateIngressMACWithFrame(bytesConstRef _cipher); void updateIngressMACWithFrame(bytesConstRef _cipher);
bi::tcp::socket& socket() { return m_socket->ref(); }
private: private:
/// Update state of _mac. /// Update state of _mac.
void updateMAC(CryptoPP::SHA3_256& _mac, bytesConstRef _seed = bytesConstRef()); void updateMAC(CryptoPP::SHA3_256& _mac, bytesConstRef _seed = bytesConstRef());
@ -125,9 +96,7 @@ private:
CryptoPP::SHA3_256 m_egressMac; ///< State of MAC for egress ciphertext. CryptoPP::SHA3_256 m_egressMac; ///< State of MAC for egress ciphertext.
CryptoPP::SHA3_256 m_ingressMac; ///< State of MAC for ingress ciphertext. CryptoPP::SHA3_256 m_ingressMac; ///< State of MAC for ingress ciphertext.
std::shared_ptr<RLPXSocket> m_socket;
}; };
} }
} }

0
libp2p/RLPXSocket.cpp

56
libp2p/RLPXSocket.h

@ -0,0 +1,56 @@
/*
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 RLPXSocket.h
* @author Alex Leverington <nessence@gmail.com>
* @date 2015
*/
#pragma once
#include "Common.h"
namespace dev
{
namespace p2p
{
/**
* @brief Shared pointer wrapper for ASIO TCP socket.
*
* Thread Safety
* Distinct Objects: Safe.
* Shared objects: Unsafe.
* * an instance method must not be called concurrently
*/
class RLPXSocket: public std::enable_shared_from_this<RLPXSocket>
{
public:
/// Constructor. Dereferences and takes ownership of _socket.
RLPXSocket(bi::tcp::socket* _socket): m_socket(std::move(*_socket)) {}
~RLPXSocket() { close(); }
bool isConnected() const { return m_socket.is_open(); }
void close() { try { boost::system::error_code ec; m_socket.shutdown(bi::tcp::socket::shutdown_both, ec); if (m_socket.is_open()) m_socket.close(); } catch (...){} }
bi::tcp::endpoint remoteEndpoint() { try { return m_socket.remote_endpoint(); } catch (...){ return bi::tcp::endpoint(); } }
bi::tcp::socket& ref() { return m_socket; }
protected:
bi::tcp::socket m_socket;
};
}
}

6
libp2p/RLPxHandshake.cpp

@ -179,7 +179,7 @@ void RLPXHandshake::transition(boost::system::error_code _ech)
/// This pointer will be freed if there is an error otherwise /// This pointer will be freed if there is an error otherwise
/// it will be passed to Host which will take ownership. /// it will be passed to Host which will take ownership.
m_io = new RLPXFrameIO(*this); m_io = new RLPXFrameCoder(*this);
// old packet format // old packet format
// 5 arguments, HelloPacket // 5 arguments, HelloPacket
@ -200,7 +200,7 @@ void RLPXHandshake::transition(boost::system::error_code _ech)
} }
else if (m_nextState == ReadHello) else if (m_nextState == ReadHello)
{ {
// Authenticate and decrypt initial hello frame with initial RLPXFrameIO // Authenticate and decrypt initial hello frame with initial RLPXFrameCoder
// and request m_host to start session. // and request m_host to start session.
m_nextState = StartSession; m_nextState = StartSession;
@ -269,7 +269,7 @@ void RLPXHandshake::transition(boost::system::error_code _ech)
try try
{ {
RLP rlp(frame.cropped(1), RLP::ThrowOnFail | RLP::FailIfTooSmall); RLP rlp(frame.cropped(1), RLP::ThrowOnFail | RLP::FailIfTooSmall);
m_host->startPeerSession(m_remote, rlp, m_io, m_socket->remoteEndpoint()); m_host->startPeerSession(m_remote, rlp, m_io, m_socket);
} }
catch (std::exception const& _e) catch (std::exception const& _e)
{ {

9
libp2p/RLPxHandshake.h

@ -25,7 +25,8 @@
#include <memory> #include <memory>
#include <libdevcrypto/Common.h> #include <libdevcrypto/Common.h>
#include <libdevcrypto/ECDHE.h> #include <libdevcrypto/ECDHE.h>
#include "RLPxFrameIO.h" #include "RLPXSocket.h"
#include "RLPXFrameCoder.h"
#include "Common.h" #include "Common.h"
namespace ba = boost::asio; namespace ba = boost::asio;
namespace bi = boost::asio::ip; namespace bi = boost::asio::ip;
@ -36,7 +37,7 @@ namespace p2p
{ {
/** /**
* @brief Setup inbound or outbound connection for communication over RLPXFrameIO. * @brief Setup inbound or outbound connection for communication over RLPXFrameCoder.
* RLPx Spec: https://github.com/ethereum/devp2p/blob/master/rlpx.md#encrypted-handshake * RLPx Spec: https://github.com/ethereum/devp2p/blob/master/rlpx.md#encrypted-handshake
* *
* @todo Implement StartSession transition via lambda which is passed to constructor. * @todo Implement StartSession transition via lambda which is passed to constructor.
@ -47,7 +48,7 @@ namespace p2p
*/ */
class RLPXHandshake: public std::enable_shared_from_this<RLPXHandshake> class RLPXHandshake: public std::enable_shared_from_this<RLPXHandshake>
{ {
friend class RLPXFrameIO; friend class RLPXFrameCoder;
/// Sequential states of handshake /// Sequential states of handshake
enum State enum State
@ -122,7 +123,7 @@ protected:
/// Used to read and write RLPx encrypted frames for last step of handshake authentication. /// Used to read and write RLPx encrypted frames for last step of handshake authentication.
/// Passed onto Host which will take ownership. /// Passed onto Host which will take ownership.
RLPXFrameIO* m_io = nullptr; RLPXFrameCoder* m_io = nullptr;
std::shared_ptr<RLPXSocket> m_socket; ///< Socket. std::shared_ptr<RLPXSocket> m_socket; ///< Socket.
boost::asio::deadline_timer m_idleTimer; ///< Timer which enforces c_timeout. boost::asio::deadline_timer m_idleTimer; ///< Timer which enforces c_timeout.

35
libp2p/Session.cpp

@ -27,23 +27,24 @@
#include <libdevcore/CommonIO.h> #include <libdevcore/CommonIO.h>
#include <libdevcore/StructuredLogger.h> #include <libdevcore/StructuredLogger.h>
#include <libethcore/Exceptions.h> #include <libethcore/Exceptions.h>
#include "RLPxHandshake.h"
#include "Host.h" #include "Host.h"
#include "Capability.h" #include "Capability.h"
using namespace std; using namespace std;
using namespace dev; using namespace dev;
using namespace dev::p2p; using namespace dev::p2p;
Session::Session(Host* _s, RLPXFrameIO* _io, std::shared_ptr<Peer> const& _n, PeerSessionInfo _info): Session::Session(Host* _h, RLPXFrameCoder* _io, std::shared_ptr<RLPXSocket> const& _s, std::shared_ptr<Peer> const& _n, PeerSessionInfo _info):
m_server(_s), m_server(_h),
m_io(_io), m_io(_io),
m_socket(m_io->socket()), m_socket(_s),
m_peer(_n), m_peer(_n),
m_info(_info), m_info(_info),
m_ping(chrono::steady_clock::time_point::max()) m_ping(chrono::steady_clock::time_point::max())
{ {
m_peer->m_lastDisconnect = NoDisconnect; m_peer->m_lastDisconnect = NoDisconnect;
m_lastReceived = m_connect = chrono::steady_clock::now(); m_lastReceived = m_connect = chrono::steady_clock::now();
m_info.socketId = _io->socket().native_handle(); m_info.socketId = m_socket->ref().native_handle();
} }
Session::~Session() Session::~Session()
@ -59,11 +60,12 @@ Session::~Session()
try try
{ {
if (m_socket.is_open()) bi::tcp::socket& socket = m_socket->ref();
if (socket.is_open())
{ {
boost::system::error_code ec; boost::system::error_code ec;
m_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
m_socket.close(); socket.close();
} }
} }
catch (...){} catch (...){}
@ -308,7 +310,7 @@ void Session::send(bytes&& _msg)
if (!checkPacket(msg)) if (!checkPacket(msg))
clog(NetWarn) << "INVALID PACKET CONSTRUCTED!"; clog(NetWarn) << "INVALID PACKET CONSTRUCTED!";
if (!m_socket.is_open()) if (!m_socket->ref().is_open())
return; return;
bool doWrite = false; bool doWrite = false;
@ -331,7 +333,7 @@ void Session::write()
out = &m_writeQueue[0]; out = &m_writeQueue[0];
} }
auto self(shared_from_this()); auto self(shared_from_this());
ba::async_write(m_socket, ba::buffer(*out), [this, self](boost::system::error_code ec, std::size_t /*length*/) ba::async_write(m_socket->ref(), ba::buffer(*out), [this, self](boost::system::error_code ec, std::size_t /*length*/)
{ {
ThreadContext tc(info().id.abridged()); ThreadContext tc(info().id.abridged());
ThreadContext tc2(info().clientVersion); ThreadContext tc2(info().clientVersion);
@ -357,13 +359,14 @@ void Session::drop(DisconnectReason _reason)
{ {
if (m_dropped) if (m_dropped)
return; return;
if (m_socket.is_open()) bi::tcp::socket& socket = m_socket->ref();
if (socket.is_open())
try try
{ {
clog(NetConnect) << "Closing " << m_socket.remote_endpoint() << "(" << reasonOf(_reason) << ")"; clog(NetConnect) << "Closing " << socket.remote_endpoint() << "(" << reasonOf(_reason) << ")";
boost::system::error_code ec; boost::system::error_code ec;
m_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
m_socket.close(); socket.close();
} }
catch (...) {} catch (...) {}
@ -384,7 +387,7 @@ void Session::disconnect(DisconnectReason _reason)
m_peer->endpoint, // TODO: may not be 100% accurate m_peer->endpoint, // TODO: may not be 100% accurate
m_server->peerCount() m_server->peerCount()
); );
if (m_socket.is_open()) if (m_socket->ref().is_open())
{ {
RLPStream s; RLPStream s;
prep(s, DisconnectPacket, 1) << (int)_reason; prep(s, DisconnectPacket, 1) << (int)_reason;
@ -406,7 +409,7 @@ void Session::doRead()
return; return;
auto self(shared_from_this()); auto self(shared_from_this());
ba::async_read(m_socket, boost::asio::buffer(m_data, h256::size), [this,self](boost::system::error_code ec, std::size_t length) ba::async_read(m_socket->ref(), boost::asio::buffer(m_data, h256::size), [this,self](boost::system::error_code ec, std::size_t length)
{ {
ThreadContext tc(info().id.abridged()); ThreadContext tc(info().id.abridged());
ThreadContext tc2(info().clientVersion); ThreadContext tc2(info().clientVersion);
@ -443,7 +446,7 @@ void Session::doRead()
/// read padded frame and mac /// read padded frame and mac
auto tlen = frameSize + ((16 - (frameSize % 16)) % 16) + h128::size; auto tlen = frameSize + ((16 - (frameSize % 16)) % 16) + h128::size;
ba::async_read(m_socket, boost::asio::buffer(m_data, tlen), [this, self, headerRLP, frameSize, tlen](boost::system::error_code ec, std::size_t length) ba::async_read(m_socket->ref(), boost::asio::buffer(m_data, tlen), [this, self, headerRLP, frameSize, tlen](boost::system::error_code ec, std::size_t length)
{ {
ThreadContext tc(info().id.abridged()); ThreadContext tc(info().id.abridged());
ThreadContext tc2(info().clientVersion); ThreadContext tc2(info().clientVersion);

11
libp2p/Session.h

@ -33,7 +33,8 @@
#include <libdevcore/RLP.h> #include <libdevcore/RLP.h>
#include <libdevcore/RangeMask.h> #include <libdevcore/RangeMask.h>
#include <libdevcore/Guards.h> #include <libdevcore/Guards.h>
#include "RLPxHandshake.h" #include "RLPXFrameCoder.h"
#include "RLPXSocket.h"
#include "Common.h" #include "Common.h"
namespace dev namespace dev
@ -55,7 +56,7 @@ class Session: public std::enable_shared_from_this<Session>
friend class HostCapabilityFace; friend class HostCapabilityFace;
public: public:
Session(Host* _server, RLPXFrameIO* _io, std::shared_ptr<Peer> const& _n, PeerSessionInfo _info); Session(Host* _server, RLPXFrameCoder* _io, std::shared_ptr<RLPXSocket> const& _s, std::shared_ptr<Peer> const& _n, PeerSessionInfo _info);
virtual ~Session(); virtual ~Session();
void start(); void start();
@ -63,7 +64,7 @@ public:
void ping(); void ping();
bool isConnected() const { return m_socket.is_open(); } bool isConnected() const { return m_socket->ref().is_open(); }
NodeId id() const; NodeId id() const;
unsigned socketId() const { return m_info.socketId; } unsigned socketId() const { return m_info.socketId; }
@ -107,8 +108,8 @@ private:
Host* m_server; ///< The host that owns us. Never null. Host* m_server; ///< The host that owns us. Never null.
RLPXFrameIO* m_io; ///< Transport over which packets are sent. RLPXFrameCoder* m_io; ///< Transport over which packets are sent.
bi::tcp::socket& m_socket; ///< Socket for the peer's connection. std::shared_ptr<RLPXSocket> m_socket; ///< Socket of peer's connection.
Mutex x_writeQueue; ///< Mutex for the write queue. Mutex x_writeQueue; ///< Mutex for the write queue.
std::deque<bytes> m_writeQueue; ///< The write queue. std::deque<bytes> m_writeQueue; ///< The write queue.
std::array<byte, 16777216> m_data; ///< Buffer for ingress packet data. std::array<byte, 16777216> m_data; ///< Buffer for ingress packet data.

38
libsolidity/AST.cpp

@ -488,7 +488,7 @@ string FunctionDefinition::externalSignature() const
bool VariableDeclaration::isLValue() const bool VariableDeclaration::isLValue() const
{ {
// External function parameters and constant declared variables are Read-Only // External function parameters and constant declared variables are Read-Only
return !isExternalFunctionParameter() && !m_isConstant; return !isExternalCallableParameter() && !m_isConstant;
} }
void VariableDeclaration::checkTypeRequirements() void VariableDeclaration::checkTypeRequirements()
@ -516,39 +516,41 @@ void VariableDeclaration::checkTypeRequirements()
BOOST_THROW_EXCEPTION(createTypeError("Assignment necessary for type detection.")); BOOST_THROW_EXCEPTION(createTypeError("Assignment necessary for type detection."));
m_value->checkTypeRequirements(nullptr); m_value->checkTypeRequirements(nullptr);
TypePointer type = m_value->getType(); TypePointer const& type = m_value->getType();
if (type->getCategory() == Type::Category::IntegerConstant) if (
{ type->getCategory() == Type::Category::IntegerConstant &&
auto intType = dynamic_pointer_cast<IntegerConstantType const>(type)->getIntegerType(); !dynamic_pointer_cast<IntegerConstantType const>(type)->getIntegerType()
if (!intType) )
BOOST_THROW_EXCEPTION(m_value->createTypeError("Invalid integer constant " + type->toString() + ".")); BOOST_THROW_EXCEPTION(m_value->createTypeError("Invalid integer constant " + type->toString() + "."));
type = intType;
}
else if (type->getCategory() == Type::Category::Void) else if (type->getCategory() == Type::Category::Void)
BOOST_THROW_EXCEPTION(createTypeError("Variable cannot have void type.")); BOOST_THROW_EXCEPTION(createTypeError("Variable cannot have void type."));
m_type = type; m_type = type->mobileType();
} }
if (m_isStateVariable && getVisibility() >= Visibility::Public && !FunctionType(*this).externalType()) if (m_isStateVariable && getVisibility() >= Visibility::Public && !FunctionType(*this).externalType())
BOOST_THROW_EXCEPTION(createTypeError("Internal type is not allowed for public state variables.")); BOOST_THROW_EXCEPTION(createTypeError("Internal type is not allowed for public state variables."));
} }
bool VariableDeclaration::isFunctionParameter() const bool VariableDeclaration::isCallableParameter() const
{ {
auto const* function = dynamic_cast<FunctionDefinition const*>(getScope()); auto const* callable = dynamic_cast<CallableDeclaration const*>(getScope());
if (!function) if (!callable)
return false; return false;
for (auto const& variable: function->getParameters() + function->getReturnParameters()) for (auto const& variable: callable->getParameters())
if (variable.get() == this) if (variable.get() == this)
return true; return true;
if (callable->getReturnParameterList())
for (auto const& variable: callable->getReturnParameterList()->getParameters())
if (variable.get() == this)
return true;
return false; return false;
} }
bool VariableDeclaration::isExternalFunctionParameter() const bool VariableDeclaration::isExternalCallableParameter() const
{ {
auto const* function = dynamic_cast<FunctionDefinition const*>(getScope()); auto const* callable = dynamic_cast<CallableDeclaration const*>(getScope());
if (!function || function->getVisibility() != Declaration::Visibility::External) if (!callable || callable->getVisibility() != Declaration::Visibility::External)
return false; return false;
for (auto const& variable: function->getParameters()) for (auto const& variable: callable->getParameters())
if (variable.get() == this) if (variable.get() == this)
return true; return true;
return false; return false;

83
libsolidity/AST.h

@ -406,13 +406,43 @@ private:
std::vector<ASTPointer<VariableDeclaration>> m_parameters; std::vector<ASTPointer<VariableDeclaration>> m_parameters;
}; };
class FunctionDefinition: public Declaration, public VariableScope, public Documented, public ImplementationOptional /**
* Base class for all nodes that define function-like objects, i.e. FunctionDefinition,
* EventDefinition and ModifierDefinition.
*/
class CallableDeclaration: public Declaration, public VariableScope
{
public:
CallableDeclaration(
SourceLocation const& _location,
ASTPointer<ASTString> const& _name,
Declaration::Visibility _visibility,
ASTPointer<ParameterList> const& _parameters,
ASTPointer<ParameterList> const& _returnParameters = ASTPointer<ParameterList>()
):
Declaration(_location, _name, _visibility),
m_parameters(_parameters),
m_returnParameters(_returnParameters)
{
}
std::vector<ASTPointer<VariableDeclaration>> const& getParameters() const { return m_parameters->getParameters(); }
ParameterList const& getParameterList() const { return *m_parameters; }
ASTPointer<ParameterList> const& getReturnParameterList() const { return m_returnParameters; }
protected:
ASTPointer<ParameterList> m_parameters;
ASTPointer<ParameterList> m_returnParameters;
};
class FunctionDefinition: public CallableDeclaration, public Documented, public ImplementationOptional
{ {
public: public:
FunctionDefinition( FunctionDefinition(
SourceLocation const& _location, SourceLocation const& _location,
ASTPointer<ASTString> const& _name, ASTPointer<ASTString> const& _name,
Declaration::Visibility _visibility, bool _isConstructor, Declaration::Visibility _visibility,
bool _isConstructor,
ASTPointer<ASTString> const& _documentation, ASTPointer<ASTString> const& _documentation,
ASTPointer<ParameterList> const& _parameters, ASTPointer<ParameterList> const& _parameters,
bool _isDeclaredConst, bool _isDeclaredConst,
@ -420,14 +450,12 @@ public:
ASTPointer<ParameterList> const& _returnParameters, ASTPointer<ParameterList> const& _returnParameters,
ASTPointer<Block> const& _body ASTPointer<Block> const& _body
): ):
Declaration(_location, _name, _visibility), CallableDeclaration(_location, _name, _visibility, _parameters, _returnParameters),
Documented(_documentation), Documented(_documentation),
ImplementationOptional(_body != nullptr), ImplementationOptional(_body != nullptr),
m_isConstructor(_isConstructor), m_isConstructor(_isConstructor),
m_parameters(_parameters),
m_isDeclaredConst(_isDeclaredConst), m_isDeclaredConst(_isDeclaredConst),
m_functionModifiers(_modifiers), m_functionModifiers(_modifiers),
m_returnParameters(_returnParameters),
m_body(_body) m_body(_body)
{} {}
@ -437,10 +465,7 @@ public:
bool isConstructor() const { return m_isConstructor; } bool isConstructor() const { return m_isConstructor; }
bool isDeclaredConst() const { return m_isDeclaredConst; } bool isDeclaredConst() const { return m_isDeclaredConst; }
std::vector<ASTPointer<ModifierInvocation>> const& getModifiers() const { return m_functionModifiers; } std::vector<ASTPointer<ModifierInvocation>> const& getModifiers() const { return m_functionModifiers; }
std::vector<ASTPointer<VariableDeclaration>> const& getParameters() const { return m_parameters->getParameters(); }
ParameterList const& getParameterList() const { return *m_parameters; }
std::vector<ASTPointer<VariableDeclaration>> const& getReturnParameters() const { return m_returnParameters->getParameters(); } std::vector<ASTPointer<VariableDeclaration>> const& getReturnParameters() const { return m_returnParameters->getParameters(); }
ASTPointer<ParameterList> const& getReturnParameterList() const { return m_returnParameters; }
Block const& getBody() const { return *m_body; } Block const& getBody() const { return *m_body; }
virtual bool isVisibleInContract() const override virtual bool isVisibleInContract() const override
@ -460,10 +485,8 @@ public:
private: private:
bool m_isConstructor; bool m_isConstructor;
ASTPointer<ParameterList> m_parameters;
bool m_isDeclaredConst; bool m_isDeclaredConst;
std::vector<ASTPointer<ModifierInvocation>> m_functionModifiers; std::vector<ASTPointer<ModifierInvocation>> m_functionModifiers;
ASTPointer<ParameterList> m_returnParameters;
ASTPointer<Block> m_body; ASTPointer<Block> m_body;
}; };
@ -512,9 +535,9 @@ public:
void checkTypeRequirements(); void checkTypeRequirements();
bool isLocalVariable() const { return !!dynamic_cast<FunctionDefinition const*>(getScope()); } bool isLocalVariable() const { return !!dynamic_cast<FunctionDefinition const*>(getScope()); }
/// @returns true if this variable is a parameter or return parameter of a function. /// @returns true if this variable is a parameter or return parameter of a function.
bool isFunctionParameter() const; bool isCallableParameter() const;
/// @returns true if this variable is a parameter (not return parameter) of an external function. /// @returns true if this variable is a parameter (not return parameter) of an external function.
bool isExternalFunctionParameter() const; bool isExternalCallableParameter() const;
bool isStateVariable() const { return m_isStateVariable; } bool isStateVariable() const { return m_isStateVariable; }
bool isIndexed() const { return m_isIndexed; } bool isIndexed() const { return m_isIndexed; }
bool isConstant() const { return m_isConstant; } bool isConstant() const { return m_isConstant; }
@ -537,22 +560,25 @@ private:
/** /**
* Definition of a function modifier. * Definition of a function modifier.
*/ */
class ModifierDefinition: public Declaration, public VariableScope, public Documented class ModifierDefinition: public CallableDeclaration, public Documented
{ {
public: public:
ModifierDefinition(SourceLocation const& _location, ModifierDefinition(
ASTPointer<ASTString> const& _name, SourceLocation const& _location,
ASTPointer<ASTString> const& _documentation, ASTPointer<ASTString> const& _name,
ASTPointer<ParameterList> const& _parameters, ASTPointer<ASTString> const& _documentation,
ASTPointer<Block> const& _body): ASTPointer<ParameterList> const& _parameters,
Declaration(_location, _name), Documented(_documentation), ASTPointer<Block> const& _body
m_parameters(_parameters), m_body(_body) {} ):
CallableDeclaration(_location, _name, Visibility::Default, _parameters),
Documented(_documentation),
m_body(_body)
{
}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override; virtual void accept(ASTConstVisitor& _visitor) const override;
std::vector<ASTPointer<VariableDeclaration>> const& getParameters() const { return m_parameters->getParameters(); }
ParameterList const& getParameterList() const { return *m_parameters; }
Block const& getBody() const { return *m_body; } Block const& getBody() const { return *m_body; }
virtual TypePointer getType(ContractDefinition const* = nullptr) const override; virtual TypePointer getType(ContractDefinition const* = nullptr) const override;
@ -560,7 +586,6 @@ public:
void checkTypeRequirements(); void checkTypeRequirements();
private: private:
ASTPointer<ParameterList> m_parameters;
ASTPointer<Block> m_body; ASTPointer<Block> m_body;
}; };
@ -591,7 +616,7 @@ private:
/** /**
* Definition of a (loggable) event. * Definition of a (loggable) event.
*/ */
class EventDefinition: public Declaration, public VariableScope, public Documented class EventDefinition: public CallableDeclaration, public Documented
{ {
public: public:
EventDefinition( EventDefinition(
@ -601,16 +626,15 @@ public:
ASTPointer<ParameterList> const& _parameters, ASTPointer<ParameterList> const& _parameters,
bool _anonymous = false bool _anonymous = false
): ):
Declaration(_location, _name), CallableDeclaration(_location, _name, Visibility::Default, _parameters),
Documented(_documentation), Documented(_documentation),
m_parameters(_parameters), m_anonymous(_anonymous)
m_anonymous(_anonymous){} {
}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override; virtual void accept(ASTConstVisitor& _visitor) const override;
std::vector<ASTPointer<VariableDeclaration>> const& getParameters() const { return m_parameters->getParameters(); }
ParameterList const& getParameterList() const { return *m_parameters; }
bool isAnonymous() const { return m_anonymous; } bool isAnonymous() const { return m_anonymous; }
virtual TypePointer getType(ContractDefinition const* = nullptr) const override virtual TypePointer getType(ContractDefinition const* = nullptr) const override
@ -621,7 +645,6 @@ public:
void checkTypeRequirements(); void checkTypeRequirements();
private: private:
ASTPointer<ParameterList> m_parameters;
bool m_anonymous = false; bool m_anonymous = false;
}; };

50
libsolidity/Compiler.cpp

@ -170,11 +170,17 @@ void Compiler::appendConstructor(FunctionDefinition const& _constructor)
if (argumentSize > 0) if (argumentSize > 0)
{ {
m_context << u256(argumentSize); CompilerUtils(m_context).fetchFreeMemoryPointer();
m_context << u256(argumentSize) << eth::Instruction::DUP1;
m_context.appendProgramSize(); m_context.appendProgramSize();
m_context << u256(CompilerUtils::dataStartOffset); // copy it to byte four as expected for ABI calls m_context << eth::Instruction::DUP4 << eth::Instruction::CODECOPY;
m_context << eth::Instruction::CODECOPY; m_context << eth::Instruction::ADD;
appendCalldataUnpacker(FunctionType(_constructor).getParameterTypes(), true); CompilerUtils(m_context).storeFreeMemoryPointer();
appendCalldataUnpacker(
FunctionType(_constructor).getParameterTypes(),
true,
CompilerUtils::freeMemoryPointer + 0x20
);
} }
_constructor.accept(*this); _constructor.accept(*this);
} }
@ -232,26 +238,35 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract)
} }
} }
void Compiler::appendCalldataUnpacker(TypePointers const& _typeParameters, bool _fromMemory) void Compiler::appendCalldataUnpacker(
TypePointers const& _typeParameters,
bool _fromMemory,
u256 _startOffset
)
{ {
// We do not check the calldata size, everything is zero-paddedd // We do not check the calldata size, everything is zero-paddedd
m_context << u256(CompilerUtils::dataStartOffset); if (_startOffset == u256(-1))
_startOffset = u256(CompilerUtils::dataStartOffset);
m_context << _startOffset;
for (TypePointer const& type: _typeParameters) for (TypePointer const& type: _typeParameters)
{ {
if (type->getCategory() == Type::Category::Array) switch (type->getCategory())
{
case Type::Category::Array:
{ {
auto const& arrayType = dynamic_cast<ArrayType const&>(*type); auto const& arrayType = dynamic_cast<ArrayType const&>(*type);
if (arrayType.location() == ReferenceType::Location::CallData) if (arrayType.location() == ReferenceType::Location::CallData)
{ {
solAssert(!_fromMemory, "");
if (type->isDynamicallySized()) if (type->isDynamicallySized())
{ {
// put on stack: data_pointer length // put on stack: data_pointer length
CompilerUtils(m_context).loadFromMemoryDynamic(IntegerType(256), !_fromMemory); CompilerUtils(m_context).loadFromMemoryDynamic(IntegerType(256), !_fromMemory);
// stack: data_offset next_pointer // stack: data_offset next_pointer
//@todo once we support nested arrays, this offset needs to be dynamic. //@todo once we support nested arrays, this offset needs to be dynamic.
m_context << eth::Instruction::SWAP1 << u256(CompilerUtils::dataStartOffset); m_context << eth::Instruction::SWAP1 << _startOffset << eth::Instruction::ADD;
m_context << eth::Instruction::ADD;
// stack: next_pointer data_pointer // stack: next_pointer data_pointer
// retrieve length // retrieve length
CompilerUtils(m_context).loadFromMemoryDynamic(IntegerType(256), !_fromMemory, true); CompilerUtils(m_context).loadFromMemoryDynamic(IntegerType(256), !_fromMemory, true);
@ -268,13 +283,15 @@ void Compiler::appendCalldataUnpacker(TypePointers const& _typeParameters, bool
else else
{ {
solAssert(arrayType.location() == ReferenceType::Location::Memory, ""); solAssert(arrayType.location() == ReferenceType::Location::Memory, "");
CompilerUtils(m_context).fetchFreeMemoryPointer(); // compute data pointer
CompilerUtils(m_context).storeInMemoryDynamic(*type); m_context << eth::Instruction::DUP1 << _startOffset << eth::Instruction::ADD;
CompilerUtils(m_context).storeFreeMemoryPointer(); if (!_fromMemory)
solAssert(false, "Not yet implemented.");
m_context << eth::Instruction::SWAP1 << u256(0x20) << eth::Instruction::ADD;
} }
break;
} }
else default:
{
solAssert(!type->isDynamicallySized(), "Unknown dynamically sized type: " + type->toString()); solAssert(!type->isDynamicallySized(), "Unknown dynamically sized type: " + type->toString());
CompilerUtils(m_context).loadFromMemoryDynamic(*type, !_fromMemory, true); CompilerUtils(m_context).loadFromMemoryDynamic(*type, !_fromMemory, true);
} }
@ -298,7 +315,10 @@ void Compiler::appendReturnValuePacker(TypePointers const& _typeParameters)
stackDepth -= type->getSizeOnStack(); stackDepth -= type->getSizeOnStack();
} }
// note that the stack is not cleaned up here // note that the stack is not cleaned up here
m_context << u256(dataOffset) << u256(0) << eth::Instruction::RETURN; if (dataOffset == 0)
m_context << eth::Instruction::STOP;
else
m_context << u256(dataOffset) << u256(0) << eth::Instruction::RETURN;
} }
void Compiler::registerStateVariables(ContractDefinition const& _contract) void Compiler::registerStateVariables(ContractDefinition const& _contract)

7
libsolidity/Compiler.h

@ -73,7 +73,12 @@ private:
void appendFunctionSelector(ContractDefinition const& _contract); void appendFunctionSelector(ContractDefinition const& _contract);
/// Creates code that unpacks the arguments for the given function represented by a vector of TypePointers. /// Creates code that unpacks the arguments for the given function represented by a vector of TypePointers.
/// From memory if @a _fromMemory is true, otherwise from call data. /// From memory if @a _fromMemory is true, otherwise from call data.
void appendCalldataUnpacker(TypePointers const& _typeParameters, bool _fromMemory = false); /// Expects source offset on the stack.
void appendCalldataUnpacker(
TypePointers const& _typeParameters,
bool _fromMemory = false,
u256 _startOffset = u256(-1)
);
void appendReturnValuePacker(TypePointers const& _typeParameters); void appendReturnValuePacker(TypePointers const& _typeParameters);
void registerStateVariables(ContractDefinition const& _contract); void registerStateVariables(ContractDefinition const& _contract);

29
libsolidity/ExpressionCompiler.cpp

@ -204,7 +204,7 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con
} }
else if (targetTypeCategory == Type::Category::Enum) else if (targetTypeCategory == Type::Category::Enum)
// just clean // just clean
appendTypeConversion(_typeOnStack, *_typeOnStack.getRealType(), true); appendTypeConversion(_typeOnStack, *_typeOnStack.mobileType(), true);
else else
{ {
solAssert(targetTypeCategory == Type::Category::Integer || targetTypeCategory == Type::Category::Contract, ""); solAssert(targetTypeCategory == Type::Category::Integer || targetTypeCategory == Type::Category::Contract, "");
@ -232,6 +232,25 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con
} }
} }
break; break;
case Type::Category::Array:
//@TODO
break;
case Type::Category::Struct:
{
solAssert(targetTypeCategory == stackTypeCategory, "");
auto& targetType = dynamic_cast<StructType const&>(_targetType);
auto& stackType = dynamic_cast<StructType const&>(_typeOnStack);
solAssert(
targetType.location() == ReferenceType::Location::Storage &&
stackType.location() == ReferenceType::Location::Storage,
"Non-storage structs not yet implemented."
);
solAssert(
targetType.isPointer(),
"Type conversion to non-pointer struct requested."
);
break;
}
default: default:
// All other types should not be convertible to non-equal types. // All other types should not be convertible to non-equal types.
solAssert(_typeOnStack == _targetType, "Invalid type conversion requested."); solAssert(_typeOnStack == _targetType, "Invalid type conversion requested.");
@ -771,7 +790,7 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
TypeType const& type = dynamic_cast<TypeType const&>(*_memberAccess.getExpression().getType()); TypeType const& type = dynamic_cast<TypeType const&>(*_memberAccess.getExpression().getType());
solAssert( solAssert(
!type.getMembers().membersByName(_memberAccess.getMemberName()).empty(), !type.getMembers().membersByName(_memberAccess.getMemberName()).empty(),
"Invalid member access to " + type.toString() "Invalid member access to " + type.toString(false)
); );
if (dynamic_cast<ContractType const*>(type.getActualType().get())) if (dynamic_cast<ContractType const*>(type.getActualType().get()))
@ -1101,7 +1120,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
bool manualFunctionId = bool manualFunctionId =
(funKind == FunctionKind::Bare || funKind == FunctionKind::BareCallCode) && (funKind == FunctionKind::Bare || funKind == FunctionKind::BareCallCode) &&
!_arguments.empty() && !_arguments.empty() &&
_arguments.front()->getType()->getRealType()->getCalldataEncodedSize(false) == _arguments.front()->getType()->mobileType()->getCalldataEncodedSize(false) ==
CompilerUtils::dataStartOffset; CompilerUtils::dataStartOffset;
if (manualFunctionId) if (manualFunctionId)
{ {
@ -1225,7 +1244,7 @@ void ExpressionCompiler::encodeToMemory(
TypePointers targetTypes = _targetTypes.empty() ? _givenTypes : _targetTypes; TypePointers targetTypes = _targetTypes.empty() ? _givenTypes : _targetTypes;
solAssert(targetTypes.size() == _givenTypes.size(), ""); solAssert(targetTypes.size() == _givenTypes.size(), "");
for (TypePointer& t: targetTypes) for (TypePointer& t: targetTypes)
t = t->getRealType()->externalType(); t = t->mobileType()->externalType();
// Stack during operation: // Stack during operation:
// <v1> <v2> ... <vn> <mem_start> <dyn_head_1> ... <dyn_head_r> <end_of_mem> // <v1> <v2> ... <vn> <mem_start> <dyn_head_1> ... <dyn_head_r> <end_of_mem>
@ -1325,7 +1344,7 @@ void ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedType,
appendTypeMoveToMemory(_expectedType); appendTypeMoveToMemory(_expectedType);
} }
else else
appendTypeMoveToMemory(*_expression.getType()->getRealType()); appendTypeMoveToMemory(*_expression.getType()->mobileType());
} }
void ExpressionCompiler::setLValueFromDeclaration(Declaration const& _declaration, Expression const& _expression) void ExpressionCompiler::setLValueFromDeclaration(Declaration const& _declaration, Expression const& _expression)

2
libsolidity/InterfaceHandler.cpp

@ -96,7 +96,7 @@ unique_ptr<string> InterfaceHandler::getABIInterface(ContractDefinition const& _
{ {
Json::Value input; Json::Value input;
input["name"] = p->getName(); input["name"] = p->getName();
input["type"] = p->getType()->toString(); input["type"] = p->getType()->toString(true);
input["indexed"] = p->isIndexed(); input["indexed"] = p->isIndexed();
params.append(input); params.append(input);
} }

6
libsolidity/LValue.cpp

@ -198,7 +198,11 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc
// stack layout: source_ref source_offset target_ref target_offset // stack layout: source_ref source_offset target_ref target_offset
// note that we have structs, so offsets should be zero and are ignored // note that we have structs, so offsets should be zero and are ignored
auto const& structType = dynamic_cast<StructType const&>(m_dataType); auto const& structType = dynamic_cast<StructType const&>(m_dataType);
solAssert(structType == _sourceType, "Struct assignment with conversion."); solAssert(
structType.structDefinition() ==
dynamic_cast<StructType const&>(_sourceType).structDefinition(),
"Struct assignment with conversion."
);
for (auto const& member: structType.getMembers()) for (auto const& member: structType.getMembers())
{ {
// assign each member that is not a mapping // assign each member that is not a mapping

14
libsolidity/NameAndTypeResolver.cpp

@ -431,7 +431,7 @@ void ReferencesResolver::endVisit(VariableDeclaration& _variable)
// They default to memory for function parameters and storage for local variables. // They default to memory for function parameters and storage for local variables.
if (auto ref = dynamic_cast<ReferenceType const*>(type.get())) if (auto ref = dynamic_cast<ReferenceType const*>(type.get()))
{ {
if (_variable.isExternalFunctionParameter()) if (_variable.isExternalCallableParameter())
{ {
// force location of external function parameters (not return) to calldata // force location of external function parameters (not return) to calldata
if (loc != Location::Default) if (loc != Location::Default)
@ -439,9 +439,9 @@ void ReferencesResolver::endVisit(VariableDeclaration& _variable)
"Location has to be calldata for external functions " "Location has to be calldata for external functions "
"(remove the \"memory\" or \"storage\" keyword)." "(remove the \"memory\" or \"storage\" keyword)."
)); ));
type = ref->copyForLocation(ReferenceType::Location::CallData); type = ref->copyForLocation(ReferenceType::Location::CallData, true);
} }
else if (_variable.isFunctionParameter() && _variable.getScope()->isPublic()) else if (_variable.isCallableParameter() && _variable.getScope()->isPublic())
{ {
// force locations of public or external function (return) parameters to memory // force locations of public or external function (return) parameters to memory
if (loc == VariableDeclaration::Location::Storage) if (loc == VariableDeclaration::Location::Storage)
@ -449,16 +449,18 @@ void ReferencesResolver::endVisit(VariableDeclaration& _variable)
"Location has to be memory for publicly visible functions " "Location has to be memory for publicly visible functions "
"(remove the \"storage\" keyword)." "(remove the \"storage\" keyword)."
)); ));
type = ref->copyForLocation(ReferenceType::Location::Memory); type = ref->copyForLocation(ReferenceType::Location::Memory, true);
} }
else else
{ {
if (loc == Location::Default) if (loc == Location::Default)
loc = _variable.isFunctionParameter() ? Location::Memory : Location::Storage; loc = _variable.isCallableParameter() ? Location::Memory : Location::Storage;
bool isPointer = !_variable.isStateVariable();
type = ref->copyForLocation( type = ref->copyForLocation(
loc == Location::Memory ? loc == Location::Memory ?
ReferenceType::Location::Memory : ReferenceType::Location::Memory :
ReferenceType::Location::Storage ReferenceType::Location::Storage,
isPointer
); );
} }
} }

158
libsolidity/Types.cpp

@ -179,6 +179,8 @@ TypePointer Type::fromMapping(ElementaryTypeName& _keyType, TypeName& _valueType
TypePointer valueType = _valueType.toType(); TypePointer valueType = _valueType.toType();
if (!valueType) if (!valueType)
BOOST_THROW_EXCEPTION(_valueType.createTypeError("Invalid type name.")); BOOST_THROW_EXCEPTION(_valueType.createTypeError("Invalid type name."));
// Convert value type to storage reference.
valueType = ReferenceType::copyForLocationIfReference(ReferenceType::Location::Storage, valueType);
return make_shared<MappingType>(keyType, valueType); return make_shared<MappingType>(keyType, valueType);
} }
@ -288,7 +290,7 @@ bool IntegerType::operator==(Type const& _other) const
return other.m_bits == m_bits && other.m_modifier == m_modifier; return other.m_bits == m_bits && other.m_modifier == m_modifier;
} }
string IntegerType::toString() const string IntegerType::toString(bool) const
{ {
if (isAddress()) if (isAddress())
return "address"; return "address";
@ -488,7 +490,7 @@ bool IntegerConstantType::operator==(Type const& _other) const
return m_value == dynamic_cast<IntegerConstantType const&>(_other).m_value; return m_value == dynamic_cast<IntegerConstantType const&>(_other).m_value;
} }
string IntegerConstantType::toString() const string IntegerConstantType::toString(bool) const
{ {
return "int_const " + m_value.str(); return "int_const " + m_value.str();
} }
@ -508,10 +510,10 @@ u256 IntegerConstantType::literalValue(Literal const*) const
return value; return value;
} }
TypePointer IntegerConstantType::getRealType() const TypePointer IntegerConstantType::mobileType() const
{ {
auto intType = getIntegerType(); auto intType = getIntegerType();
solAssert(!!intType, "getRealType called with invalid integer constant " + toString()); solAssert(!!intType, "mobileType called with invalid integer constant " + toString(false));
return intType; return intType;
} }
@ -668,21 +670,67 @@ TypePointer ContractType::unaryOperatorResult(Token::Value _operator) const
return _operator == Token::Delete ? make_shared<VoidType>() : TypePointer(); return _operator == Token::Delete ? make_shared<VoidType>() : TypePointer();
} }
TypePointer ReferenceType::copyForLocationIfReference(Location _location, TypePointer const& _type)
{
if (auto type = dynamic_cast<ReferenceType const*>(_type.get()))
return type->copyForLocation(_location, false);
return _type;
}
TypePointer ReferenceType::copyForLocationIfReference(TypePointer const& _type) const
{
return copyForLocationIfReference(m_location, _type);
}
string ReferenceType::stringForReferencePart() const
{
switch (m_location)
{
case Location::Storage:
return string("storage ") + (m_isPointer ? "pointer" : "ref");
case Location::CallData:
return "calldata";
case Location::Memory:
return "memory";
}
solAssert(false, "");
return "";
}
bool ArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const bool ArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const
{ {
if (_convertTo.getCategory() != getCategory()) if (_convertTo.getCategory() != getCategory())
return false; return false;
auto& convertTo = dynamic_cast<ArrayType const&>(_convertTo); auto& convertTo = dynamic_cast<ArrayType const&>(_convertTo);
// let us not allow assignment to memory arrays for now
if (convertTo.location() != Location::Storage)
return false;
if (convertTo.isByteArray() != isByteArray() || convertTo.isString() != isString()) if (convertTo.isByteArray() != isByteArray() || convertTo.isString() != isString())
return false; return false;
if (!getBaseType()->isImplicitlyConvertibleTo(*convertTo.getBaseType())) // memory/calldata to storage can be converted, but only to a direct storage reference
if (convertTo.location() == Location::Storage && location() != Location::Storage && convertTo.isPointer())
return false; return false;
if (convertTo.isDynamicallySized()) if (convertTo.location() == Location::CallData && location() != convertTo.location())
return false;
if (convertTo.location() == Location::Storage && !convertTo.isPointer())
{
// Less restrictive conversion, since we need to copy anyway.
if (!getBaseType()->isImplicitlyConvertibleTo(*convertTo.getBaseType()))
return false;
if (convertTo.isDynamicallySized())
return true;
return !isDynamicallySized() && convertTo.getLength() >= getLength();
}
else
{
// Require that the base type is the same, not only convertible.
// This disallows assignment of nested arrays from storage to memory for now.
if (*getBaseType() != *convertTo.getBaseType())
return false;
if (isDynamicallySized() != convertTo.isDynamicallySized())
return false;
// We also require that the size is the same.
if (!isDynamicallySized() && getLength() != convertTo.getLength())
return false;
return true; return true;
return !isDynamicallySized() && convertTo.getLength() >= getLength(); }
} }
TypePointer ArrayType::unaryOperatorResult(Token::Value _operator) const TypePointer ArrayType::unaryOperatorResult(Token::Value _operator) const
@ -698,7 +746,7 @@ bool ArrayType::operator==(Type const& _other) const
return false; return false;
ArrayType const& other = dynamic_cast<ArrayType const&>(_other); ArrayType const& other = dynamic_cast<ArrayType const&>(_other);
if ( if (
other.m_location != m_location || !ReferenceType::operator==(other) ||
other.isByteArray() != isByteArray() || other.isByteArray() != isByteArray() ||
other.isString() != isString() || other.isString() != isString() ||
other.isDynamicallySized() != isDynamicallySized() other.isDynamicallySized() != isDynamicallySized()
@ -751,16 +799,23 @@ unsigned ArrayType::getSizeOnStack() const
return 1; return 1;
} }
string ArrayType::toString() const string ArrayType::toString(bool _short) const
{ {
string ret;
if (isString()) if (isString())
return "string"; ret = "string";
else if (isByteArray()) else if (isByteArray())
return "bytes"; ret = "bytes";
string ret = getBaseType()->toString() + "["; else
if (!isDynamicallySized()) {
ret += getLength().str(); ret = getBaseType()->toString(_short) + "[";
return ret + "]"; if (!isDynamicallySized())
ret += getLength().str();
ret += "]";
}
if (!_short)
ret += " " + stringForReferencePart();
return ret;
} }
TypePointer ArrayType::externalType() const TypePointer ArrayType::externalType() const
@ -778,14 +833,12 @@ TypePointer ArrayType::externalType() const
return std::make_shared<ArrayType>(Location::CallData, m_baseType->externalType(), m_length); return std::make_shared<ArrayType>(Location::CallData, m_baseType->externalType(), m_length);
} }
TypePointer ArrayType::copyForLocation(ReferenceType::Location _location) const TypePointer ArrayType::copyForLocation(ReferenceType::Location _location, bool _isPointer) const
{ {
auto copy = make_shared<ArrayType>(_location); auto copy = make_shared<ArrayType>(_location);
copy->m_isPointer = _isPointer;
copy->m_arrayKind = m_arrayKind; copy->m_arrayKind = m_arrayKind;
if (auto ref = dynamic_cast<ReferenceType const*>(m_baseType.get())) copy->m_baseType = copy->copyForLocationIfReference(m_baseType);
copy->m_baseType = ref->copyForLocation(_location);
else
copy->m_baseType = m_baseType;
copy->m_hasDynamicLength = m_hasDynamicLength; copy->m_hasDynamicLength = m_hasDynamicLength;
copy->m_length = m_length; copy->m_length = m_length;
return copy; return copy;
@ -801,7 +854,7 @@ bool ContractType::operator==(Type const& _other) const
return other.m_contract == m_contract && other.m_super == m_super; return other.m_contract == m_contract && other.m_super == m_super;
} }
string ContractType::toString() const string ContractType::toString(bool) const
{ {
return "contract " + string(m_super ? "super " : "") + m_contract.getName(); return "contract " + string(m_super ? "super " : "") + m_contract.getName();
} }
@ -890,6 +943,19 @@ vector<tuple<VariableDeclaration const*, u256, unsigned>> ContractType::getState
return variablesAndOffsets; return variablesAndOffsets;
} }
bool StructType::isImplicitlyConvertibleTo(const Type& _convertTo) const
{
if (_convertTo.getCategory() != getCategory())
return false;
auto& convertTo = dynamic_cast<StructType const&>(_convertTo);
// memory/calldata to storage can be converted, but only to a direct storage reference
if (convertTo.location() == Location::Storage && location() != Location::Storage && convertTo.isPointer())
return false;
if (convertTo.location() == Location::CallData && location() != convertTo.location())
return false;
return this->m_struct == convertTo.m_struct;
}
TypePointer StructType::unaryOperatorResult(Token::Value _operator) const TypePointer StructType::unaryOperatorResult(Token::Value _operator) const
{ {
return _operator == Token::Delete ? make_shared<VoidType>() : TypePointer(); return _operator == Token::Delete ? make_shared<VoidType>() : TypePointer();
@ -900,7 +966,7 @@ bool StructType::operator==(Type const& _other) const
if (_other.getCategory() != getCategory()) if (_other.getCategory() != getCategory())
return false; return false;
StructType const& other = dynamic_cast<StructType const&>(_other); StructType const& other = dynamic_cast<StructType const&>(_other);
return other.m_struct == m_struct; return ReferenceType::operator==(other) && other.m_struct == m_struct;
} }
u256 StructType::getStorageSize() const u256 StructType::getStorageSize() const
@ -916,9 +982,12 @@ bool StructType::canLiveOutsideStorage() const
return true; return true;
} }
string StructType::toString() const string StructType::toString(bool _short) const
{ {
return string("struct ") + m_struct.getName(); string ret = "struct " + m_struct.getName();
if (!_short)
ret += " " + stringForReferencePart();
return ret;
} }
MemberList const& StructType::getMembers() const MemberList const& StructType::getMembers() const
@ -928,16 +997,23 @@ MemberList const& StructType::getMembers() const
{ {
MemberList::MemberMap members; MemberList::MemberMap members;
for (ASTPointer<VariableDeclaration> const& variable: m_struct.getMembers()) for (ASTPointer<VariableDeclaration> const& variable: m_struct.getMembers())
members.push_back(MemberList::Member(variable->getName(), variable->getType(), variable.get())); {
members.push_back(MemberList::Member(
variable->getName(),
copyForLocationIfReference(variable->getType()),
variable.get())
);
}
m_members.reset(new MemberList(members)); m_members.reset(new MemberList(members));
} }
return *m_members; return *m_members;
} }
TypePointer StructType::copyForLocation(ReferenceType::Location _location) const TypePointer StructType::copyForLocation(ReferenceType::Location _location, bool _isPointer) const
{ {
auto copy = make_shared<StructType>(m_struct); auto copy = make_shared<StructType>(m_struct);
copy->m_location = _location; copy->m_location = _location;
copy->m_isPointer = _isPointer;
return copy; return copy;
} }
@ -970,7 +1046,7 @@ unsigned EnumType::getStorageBytes() const
return dev::bytesRequired(elements - 1); return dev::bytesRequired(elements - 1);
} }
string EnumType::toString() const string EnumType::toString(bool) const
{ {
return string("enum ") + m_enum.getName(); return string("enum ") + m_enum.getName();
} }
@ -1114,14 +1190,14 @@ bool FunctionType::operator==(Type const& _other) const
return true; return true;
} }
string FunctionType::toString() const string FunctionType::toString(bool _short) const
{ {
string name = "function ("; string name = "function (";
for (auto it = m_parameterTypes.begin(); it != m_parameterTypes.end(); ++it) for (auto it = m_parameterTypes.begin(); it != m_parameterTypes.end(); ++it)
name += (*it)->toString() + (it + 1 == m_parameterTypes.end() ? "" : ","); name += (*it)->toString(_short) + (it + 1 == m_parameterTypes.end() ? "" : ",");
name += ") returns ("; name += ") returns (";
for (auto it = m_returnParameterTypes.begin(); it != m_returnParameterTypes.end(); ++it) for (auto it = m_returnParameterTypes.begin(); it != m_returnParameterTypes.end(); ++it)
name += (*it)->toString() + (it + 1 == m_returnParameterTypes.end() ? "" : ","); name += (*it)->toString(_short) + (it + 1 == m_returnParameterTypes.end() ? "" : ",");
return name + ")"; return name + ")";
} }
@ -1289,7 +1365,7 @@ string FunctionType::externalSignature(std::string const& _name) const
for (auto it = externalParameterTypes.cbegin(); it != externalParameterTypes.cend(); ++it) for (auto it = externalParameterTypes.cbegin(); it != externalParameterTypes.cend(); ++it)
{ {
solAssert(!!(*it), "Parameter should have external type"); solAssert(!!(*it), "Parameter should have external type");
ret += (*it)->toString() + (it + 1 == externalParameterTypes.cend() ? "" : ","); ret += (*it)->toString(true) + (it + 1 == externalParameterTypes.cend() ? "" : ",");
} }
return ret + ")"; return ret + ")";
@ -1327,7 +1403,7 @@ vector<string> const FunctionType::getParameterTypeNames() const
{ {
vector<string> names; vector<string> names;
for (TypePointer const& t: m_parameterTypes) for (TypePointer const& t: m_parameterTypes)
names.push_back(t->toString()); names.push_back(t->toString(true));
return names; return names;
} }
@ -1336,7 +1412,7 @@ vector<string> const FunctionType::getReturnParameterTypeNames() const
{ {
vector<string> names; vector<string> names;
for (TypePointer const& t: m_returnParameterTypes) for (TypePointer const& t: m_returnParameterTypes)
names.push_back(t->toString()); names.push_back(t->toString(true));
return names; return names;
} }
@ -1358,9 +1434,9 @@ bool MappingType::operator==(Type const& _other) const
return *other.m_keyType == *m_keyType && *other.m_valueType == *m_valueType; return *other.m_keyType == *m_keyType && *other.m_valueType == *m_valueType;
} }
string MappingType::toString() const string MappingType::toString(bool _short) const
{ {
return "mapping(" + getKeyType()->toString() + " => " + getValueType()->toString() + ")"; return "mapping(" + getKeyType()->toString(_short) + " => " + getValueType()->toString(_short) + ")";
} }
u256 VoidType::getStorageSize() const u256 VoidType::getStorageSize() const
@ -1445,11 +1521,11 @@ bool ModifierType::operator==(Type const& _other) const
return true; return true;
} }
string ModifierType::toString() const string ModifierType::toString(bool _short) const
{ {
string name = "modifier ("; string name = "modifier (";
for (auto it = m_parameterTypes.begin(); it != m_parameterTypes.end(); ++it) for (auto it = m_parameterTypes.begin(); it != m_parameterTypes.end(); ++it)
name += (*it)->toString() + (it + 1 == m_parameterTypes.end() ? "" : ","); name += (*it)->toString(_short) + (it + 1 == m_parameterTypes.end() ? "" : ",");
return name + ")"; return name + ")";
} }
@ -1496,7 +1572,7 @@ bool MagicType::operator==(Type const& _other) const
return other.m_kind == m_kind; return other.m_kind == m_kind;
} }
string MagicType::toString() const string MagicType::toString(bool) const
{ {
switch (m_kind) switch (m_kind)
{ {

103
libsolidity/Types.h

@ -198,19 +198,24 @@ public:
/// i.e. it behaves differently in lvalue context and in value context. /// i.e. it behaves differently in lvalue context and in value context.
virtual bool isValueType() const { return false; } virtual bool isValueType() const { return false; }
virtual unsigned getSizeOnStack() const { return 1; } virtual unsigned getSizeOnStack() const { return 1; }
/// @returns the real type of some types, like e.g: IntegerConstant /// @returns the mobile (in contrast to static) type corresponding to the given type.
virtual TypePointer getRealType() const { return shared_from_this(); } /// This returns the corresponding integer type for IntegerConstantTypes and the pointer type
/// for storage reference types.
virtual TypePointer mobileType() const { return shared_from_this(); }
/// Returns the list of all members of this type. Default implementation: no members. /// Returns the list of all members of this type. Default implementation: no members.
virtual MemberList const& getMembers() const { return EmptyMemberList; } virtual MemberList const& getMembers() const { return EmptyMemberList; }
/// Convenience method, returns the type of the given named member or an empty pointer if no such member exists. /// Convenience method, returns the type of the given named member or an empty pointer if no such member exists.
TypePointer getMemberType(std::string const& _name) const { return getMembers().getMemberType(_name); } TypePointer getMemberType(std::string const& _name) const { return getMembers().getMemberType(_name); }
virtual std::string toString() const = 0; virtual std::string toString(bool _short) const = 0;
std::string toString() const { return toString(false); }
virtual u256 literalValue(Literal const*) const virtual u256 literalValue(Literal const*) const
{ {
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Literal value requested " BOOST_THROW_EXCEPTION(
"for type without literals.")); InternalCompilerError() <<
errinfo_comment("Literal value requested for type without literals.")
);
} }
/// @returns a type suitable for outside of Solidity, i.e. for contract types it returns address. /// @returns a type suitable for outside of Solidity, i.e. for contract types it returns address.
@ -249,7 +254,7 @@ public:
virtual MemberList const& getMembers() const override { return isAddress() ? AddressMemberList : EmptyMemberList; } virtual MemberList const& getMembers() const override { return isAddress() ? AddressMemberList : EmptyMemberList; }
virtual std::string toString() const override; virtual std::string toString(bool _short) const override;
virtual TypePointer externalType() const override { return shared_from_this(); } virtual TypePointer externalType() const override { return shared_from_this(); }
@ -287,9 +292,9 @@ public:
virtual bool canLiveOutsideStorage() const override { return false; } virtual bool canLiveOutsideStorage() const override { return false; }
virtual unsigned getSizeOnStack() const override { return 1; } virtual unsigned getSizeOnStack() const override { return 1; }
virtual std::string toString() const override; virtual std::string toString(bool _short) const override;
virtual u256 literalValue(Literal const* _literal) const override; virtual u256 literalValue(Literal const* _literal) const override;
virtual TypePointer getRealType() const override; virtual TypePointer mobileType() const override;
/// @returns the smallest integer type that can hold the value or an empty pointer if not possible. /// @returns the smallest integer type that can hold the value or an empty pointer if not possible.
std::shared_ptr<IntegerType const> getIntegerType() const; std::shared_ptr<IntegerType const> getIntegerType() const;
@ -322,7 +327,7 @@ public:
virtual unsigned getStorageBytes() const override { return m_bytes; } virtual unsigned getStorageBytes() const override { return m_bytes; }
virtual bool isValueType() const override { return true; } virtual bool isValueType() const override { return true; }
virtual std::string toString() const override { return "bytes" + dev::toString(m_bytes); } virtual std::string toString(bool) const override { return "bytes" + dev::toString(m_bytes); }
virtual u256 literalValue(Literal const* _literal) const override; virtual u256 literalValue(Literal const* _literal) const override;
virtual TypePointer externalType() const override { return shared_from_this(); } virtual TypePointer externalType() const override { return shared_from_this(); }
@ -348,27 +353,51 @@ public:
virtual unsigned getStorageBytes() const override { return 1; } virtual unsigned getStorageBytes() const override { return 1; }
virtual bool isValueType() const override { return true; } virtual bool isValueType() const override { return true; }
virtual std::string toString() const override { return "bool"; } virtual std::string toString(bool) const override { return "bool"; }
virtual u256 literalValue(Literal const* _literal) const override; virtual u256 literalValue(Literal const* _literal) const override;
virtual TypePointer externalType() const override { return shared_from_this(); } virtual TypePointer externalType() const override { return shared_from_this(); }
}; };
/** /**
* Trait used by types which are not value types and can be stored either in storage, memory * Base class used by types which are not value types and can be stored either in storage, memory
* or calldata. This is currently used by arrays and structs. * or calldata. This is currently used by arrays and structs.
*/ */
class ReferenceType class ReferenceType: public Type
{ {
public: public:
enum class Location { Storage, CallData, Memory }; enum class Location { Storage, CallData, Memory };
explicit ReferenceType(Location _location): m_location(_location) {} explicit ReferenceType(Location _location): m_location(_location) {}
Location location() const { return m_location; } Location location() const { return m_location; }
/// @returns a copy of this type with location (recursively) changed to @a _location. /// @returns a copy of this type with location (recursively) changed to @a _location,
virtual TypePointer copyForLocation(Location _location) const = 0; /// whereas isPointer is only shallowly changed - the deep copy is always a bound reference.
virtual TypePointer copyForLocation(Location _location, bool _isPointer) const = 0;
virtual TypePointer mobileType() const override { return copyForLocation(m_location, true); }
/// Storage references can be pointers or bound references. In general, local variables are of
/// pointer type, state variables are bound references. Assignments to pointers or deleting
/// them will not modify storage (that will only change the pointer). Assignment from
/// non-storage objects to a variable of storage pointer type is not possible.
bool isPointer() const { return m_isPointer; }
bool operator==(ReferenceType const& _other) const
{
return location() == _other.location() && isPointer() == _other.isPointer();
}
/// @returns a copy of @a _type having the same location as this (and is not a pointer type)
/// if _type is a reference type and an unmodified copy of _type otherwise.
/// This function is mostly useful to modify inner types appropriately.
static TypePointer copyForLocationIfReference(Location _location, TypePointer const& _type);
protected: protected:
TypePointer copyForLocationIfReference(TypePointer const& _type) const;
/// @returns a human-readable description of the reference part of the type.
std::string stringForReferencePart() const;
Location m_location = Location::Storage; Location m_location = Location::Storage;
bool m_isPointer = true;
}; };
/** /**
@ -378,10 +407,9 @@ protected:
* one slot). Dynamically sized arrays (including byte arrays) start with their size as a uint and * one slot). Dynamically sized arrays (including byte arrays) start with their size as a uint and
* thus start on their own slot. * thus start on their own slot.
*/ */
class ArrayType: public Type, public ReferenceType class ArrayType: public ReferenceType
{ {
public: public:
virtual Category getCategory() const override { return Category::Array; } virtual Category getCategory() const override { return Category::Array; }
/// Constructor for a byte array ("bytes") and string. /// Constructor for a byte array ("bytes") and string.
@ -389,16 +417,18 @@ public:
ReferenceType(_location), ReferenceType(_location),
m_arrayKind(_isString ? ArrayKind::String : ArrayKind::Bytes), m_arrayKind(_isString ? ArrayKind::String : ArrayKind::Bytes),
m_baseType(std::make_shared<FixedBytesType>(1)) m_baseType(std::make_shared<FixedBytesType>(1))
{} {
}
/// Constructor for a dynamically sized array type ("type[]") /// Constructor for a dynamically sized array type ("type[]")
ArrayType(Location _location, const TypePointer &_baseType): ArrayType(Location _location, TypePointer const& _baseType):
ReferenceType(_location), ReferenceType(_location),
m_baseType(_baseType) m_baseType(copyForLocationIfReference(_baseType))
{} {
}
/// Constructor for a fixed-size array type ("type[20]") /// Constructor for a fixed-size array type ("type[20]")
ArrayType(Location _location, const TypePointer &_baseType, u256 const& _length): ArrayType(Location _location, TypePointer const& _baseType, u256 const& _length):
ReferenceType(_location), ReferenceType(_location),
m_baseType(_baseType), m_baseType(copyForLocationIfReference(_baseType)),
m_hasDynamicLength(false), m_hasDynamicLength(false),
m_length(_length) m_length(_length)
{} {}
@ -410,7 +440,7 @@ public:
virtual bool isDynamicallySized() const override { return m_hasDynamicLength; } virtual bool isDynamicallySized() const override { return m_hasDynamicLength; }
virtual u256 getStorageSize() const override; virtual u256 getStorageSize() const override;
virtual unsigned getSizeOnStack() const override; virtual unsigned getSizeOnStack() const override;
virtual std::string toString() const override; virtual std::string toString(bool _short) const override;
virtual MemberList const& getMembers() const override virtual MemberList const& getMembers() const override
{ {
return isString() ? EmptyMemberList : s_arrayTypeMemberList; return isString() ? EmptyMemberList : s_arrayTypeMemberList;
@ -424,7 +454,7 @@ public:
TypePointer const& getBaseType() const { solAssert(!!m_baseType, ""); return m_baseType;} TypePointer const& getBaseType() const { solAssert(!!m_baseType, ""); return m_baseType;}
u256 const& getLength() const { return m_length; } u256 const& getLength() const { return m_length; }
TypePointer copyForLocation(Location _location) const override; TypePointer copyForLocation(Location _location, bool _isPointer) const override;
private: private:
/// String is interpreted as a subtype of Bytes. /// String is interpreted as a subtype of Bytes.
@ -460,7 +490,7 @@ public:
virtual unsigned getStorageBytes() const override { return 20; } virtual unsigned getStorageBytes() const override { return 20; }
virtual bool canLiveOutsideStorage() const override { return true; } virtual bool canLiveOutsideStorage() const override { return true; }
virtual bool isValueType() const override { return true; } virtual bool isValueType() const override { return true; }
virtual std::string toString() const override; virtual std::string toString(bool _short) const override;
virtual MemberList const& getMembers() const override; virtual MemberList const& getMembers() const override;
virtual TypePointer externalType() const override virtual TypePointer externalType() const override
@ -497,26 +527,29 @@ private:
/** /**
* The type of a struct instance, there is one distinct type per struct definition. * The type of a struct instance, there is one distinct type per struct definition.
*/ */
class StructType: public Type, public ReferenceType class StructType: public ReferenceType
{ {
public: public:
virtual Category getCategory() const override { return Category::Struct; } virtual Category getCategory() const override { return Category::Struct; }
explicit StructType(StructDefinition const& _struct): explicit StructType(StructDefinition const& _struct):
//@todo only storage until we have non-storage structs //@todo only storage until we have non-storage structs
ReferenceType(Location::Storage), m_struct(_struct) {} ReferenceType(Location::Storage), m_struct(_struct) {}
virtual bool isImplicitlyConvertibleTo(const Type& _convertTo) const override;
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
virtual bool operator==(Type const& _other) const override; virtual bool operator==(Type const& _other) const override;
virtual u256 getStorageSize() const override; virtual u256 getStorageSize() const override;
virtual bool canLiveOutsideStorage() const override; virtual bool canLiveOutsideStorage() const override;
virtual unsigned getSizeOnStack() const override { return 2; } virtual unsigned getSizeOnStack() const override { return 2; }
virtual std::string toString() const override; virtual std::string toString(bool _short) const override;
virtual MemberList const& getMembers() const override; virtual MemberList const& getMembers() const override;
TypePointer copyForLocation(Location _location) const override; TypePointer copyForLocation(Location _location, bool _isPointer) const override;
std::pair<u256, unsigned> const& getStorageOffsetsOfMember(std::string const& _name) const; std::pair<u256, unsigned> const& getStorageOffsetsOfMember(std::string const& _name) const;
StructDefinition const& structDefinition() const { return m_struct; }
private: private:
StructDefinition const& m_struct; StructDefinition const& m_struct;
/// List of member types, will be lazy-initialized because of recursive references. /// List of member types, will be lazy-initialized because of recursive references.
@ -540,7 +573,7 @@ public:
virtual unsigned getSizeOnStack() const override { return 1; } virtual unsigned getSizeOnStack() const override { return 1; }
virtual unsigned getStorageBytes() const override; virtual unsigned getStorageBytes() const override;
virtual bool canLiveOutsideStorage() const override { return true; } virtual bool canLiveOutsideStorage() const override { return true; }
virtual std::string toString() const override; virtual std::string toString(bool _short) const override;
virtual bool isValueType() const override { return true; } virtual bool isValueType() const override { return true; }
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
@ -649,7 +682,7 @@ public:
std::vector<std::string> const getReturnParameterTypeNames() const; std::vector<std::string> const getReturnParameterTypeNames() const;
virtual bool operator==(Type const& _other) const override; virtual bool operator==(Type const& _other) const override;
virtual std::string toString() const override; virtual std::string toString(bool _short) const override;
virtual bool canBeStored() const override { return false; } virtual bool canBeStored() const override { return false; }
virtual u256 getStorageSize() const override; virtual u256 getStorageSize() const override;
virtual bool canLiveOutsideStorage() const override { return false; } virtual bool canLiveOutsideStorage() const override { return false; }
@ -721,7 +754,7 @@ public:
m_keyType(_keyType), m_valueType(_valueType) {} m_keyType(_keyType), m_valueType(_valueType) {}
virtual bool operator==(Type const& _other) const override; virtual bool operator==(Type const& _other) const override;
virtual std::string toString() const override; virtual std::string toString(bool _short) const override;
virtual unsigned getSizeOnStack() const override { return 2; } virtual unsigned getSizeOnStack() const override { return 2; }
virtual bool canLiveOutsideStorage() const override { return false; } virtual bool canLiveOutsideStorage() const override { return false; }
@ -744,7 +777,7 @@ public:
VoidType() {} VoidType() {}
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
virtual std::string toString() const override { return "void"; } virtual std::string toString(bool) const override { return "void"; }
virtual bool canBeStored() const override { return false; } virtual bool canBeStored() const override { return false; }
virtual u256 getStorageSize() const override; virtual u256 getStorageSize() const override;
virtual bool canLiveOutsideStorage() const override { return false; } virtual bool canLiveOutsideStorage() const override { return false; }
@ -769,7 +802,7 @@ public:
virtual u256 getStorageSize() const override; virtual u256 getStorageSize() const override;
virtual bool canLiveOutsideStorage() const override { return false; } virtual bool canLiveOutsideStorage() const override { return false; }
virtual unsigned getSizeOnStack() const override { return 0; } virtual unsigned getSizeOnStack() const override { return 0; }
virtual std::string toString() const override { return "type(" + m_actualType->toString() + ")"; } virtual std::string toString(bool _short) const override { return "type(" + m_actualType->toString(_short) + ")"; }
virtual MemberList const& getMembers() const override; virtual MemberList const& getMembers() const override;
private: private:
@ -796,7 +829,7 @@ public:
virtual bool canLiveOutsideStorage() const override { return false; } virtual bool canLiveOutsideStorage() const override { return false; }
virtual unsigned getSizeOnStack() const override { return 0; } virtual unsigned getSizeOnStack() const override { return 0; }
virtual bool operator==(Type const& _other) const override; virtual bool operator==(Type const& _other) const override;
virtual std::string toString() const override; virtual std::string toString(bool _short) const override;
private: private:
TypePointers m_parameterTypes; TypePointers m_parameterTypes;
@ -826,7 +859,7 @@ public:
virtual unsigned getSizeOnStack() const override { return 0; } virtual unsigned getSizeOnStack() const override { return 0; }
virtual MemberList const& getMembers() const override { return m_members; } virtual MemberList const& getMembers() const override { return m_members; }
virtual std::string toString() const override; virtual std::string toString(bool _short) const override;
private: private:
Kind m_kind; Kind m_kind;

4
libtestutils/FixedClient.cpp

@ -28,5 +28,7 @@ using namespace dev::test;
eth::State FixedClient::asOf(h256 const& _h) const eth::State FixedClient::asOf(h256 const& _h) const
{ {
ReadGuard l(x_stateDB); ReadGuard l(x_stateDB);
return State(m_state.db(), bc(), _h); State ret(m_state.db());
ret.populateFromChain(bc(), _h);
return ret;
} }

427
libweb3jsonrpc/JsonHelper.cpp

@ -0,0 +1,427 @@
/*
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 JsonHelper.cpp
* @authors:
* Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "JsonHelper.h"
#include <libevmcore/Instruction.h>
#include <liblll/Compiler.h>
#include <libethereum/Client.h>
#include <libwebthree/WebThree.h>
#include <libethcore/CommonJS.h>
#include <libwhisper/Message.h>
#include <libwhisper/WhisperHost.h>
using namespace std;
using namespace dev;
using namespace eth;
namespace dev
{
Json::Value toJson(unordered_map<u256, u256> const& _storage)
{
Json::Value res(Json::objectValue);
for (auto i: _storage)
res[toJS(i.first)] = toJS(i.second);
return res;
}
Json::Value toJson(map<u256, u256> const& _storage)
{
Json::Value res(Json::objectValue);
for (auto i: _storage)
res[toJS(i.first)] = toJS(i.second);
return res;
}
// ////////////////////////////////////////////////////////////////////////////////
// p2p
// ////////////////////////////////////////////////////////////////////////////////
namespace p2p
{
Json::Value toJson(p2p::PeerSessionInfo const& _p)
{
Json::Value ret;
ret["id"] = _p.id.hex();
ret["clientVersion"] = _p.clientVersion;
ret["host"] = _p.host;
ret["port"] = _p.port;
ret["lastPing"] = (int)chrono::duration_cast<chrono::milliseconds>(_p.lastPing).count();
for (auto const& i: _p.notes)
ret["notes"][i.first] = i.second;
for (auto const& i: _p.caps)
ret["caps"][i.first] = (unsigned)i.second;
return ret;
}
}
// ////////////////////////////////////////////////////////////////////////////////
// eth
// ////////////////////////////////////////////////////////////////////////////////
namespace eth
{
Json::Value toJson(dev::eth::BlockInfo const& _bi)
{
Json::Value res;
if (_bi)
{
res["hash"] = toJS(_bi.hash());
res["parentHash"] = toJS(_bi.parentHash);
res["sha3Uncles"] = toJS(_bi.sha3Uncles);
res["miner"] = toJS(_bi.coinbaseAddress);
res["stateRoot"] = toJS(_bi.stateRoot);
res["transactionsRoot"] = toJS(_bi.transactionsRoot);
res["difficulty"] = toJS(_bi.difficulty);
res["number"] = toJS(_bi.number);
res["gasUsed"] = toJS(_bi.gasUsed);
res["gasLimit"] = toJS(_bi.gasLimit);
res["timestamp"] = toJS(_bi.timestamp);
res["extraData"] = toJS(_bi.extraData);
res["nonce"] = toJS(_bi.nonce);
res["logsBloom"] = toJS(_bi.logBloom);
res["seedHash"] = toJS(_bi.seedHash());
res["target"] = toJS(_bi.boundary());
}
return res;
}
Json::Value toJson(dev::eth::Transaction const& _t, std::pair<h256, unsigned> _location, BlockNumber _blockNumber)
{
Json::Value res;
if (_t)
{
res["hash"] = toJS(_t.sha3());
res["input"] = toJS(_t.data());
res["to"] = _t.isCreation() ? Json::Value() : toJS(_t.receiveAddress());
res["from"] = toJS(_t.safeSender());
res["gas"] = toJS(_t.gas());
res["gasPrice"] = toJS(_t.gasPrice());
res["nonce"] = toJS(_t.nonce());
res["value"] = toJS(_t.value());
res["blockHash"] = toJS(_location.first);
res["transactionIndex"] = toJS(_location.second);
res["blockNumber"] = toJS(_blockNumber);
}
return res;
}
Json::Value toJson(dev::eth::BlockInfo const& _bi, BlockDetails const& _bd, UncleHashes const& _us, Transactions const& _ts)
{
Json::Value res = toJson(_bi);
if (_bi)
{
res["totalDifficulty"] = toJS(_bd.totalDifficulty);
res["uncles"] = Json::Value(Json::arrayValue);
for (h256 h: _us)
res["uncles"].append(toJS(h));
res["transactions"] = Json::Value(Json::arrayValue);
for (unsigned i = 0; i < _ts.size(); i++)
res["transactions"].append(toJson(_ts[i], std::make_pair(_bi.hash(), i), (BlockNumber)_bi.number));
}
return res;
}
Json::Value toJson(dev::eth::BlockInfo const& _bi, BlockDetails const& _bd, UncleHashes const& _us, TransactionHashes const& _ts)
{
Json::Value res = toJson(_bi);
if (_bi)
{
res["totalDifficulty"] = toJS(_bd.totalDifficulty);
res["uncles"] = Json::Value(Json::arrayValue);
for (h256 h: _us)
res["uncles"].append(toJS(h));
res["transactions"] = Json::Value(Json::arrayValue);
for (h256 const& t: _ts)
res["transactions"].append(toJS(t));
}
return res;
}
Json::Value toJson(dev::eth::TransactionSkeleton const& _t)
{
Json::Value res;
res["to"] = _t.creation ? Json::Value() : toJS(_t.to);
res["from"] = toJS(_t.from);
res["gas"] = toJS(_t.gas);
res["gasPrice"] = toJS(_t.gasPrice);
res["value"] = toJS(_t.value);
res["data"] = toJS(_t.data, 32);
return res;
}
Json::Value toJson(dev::eth::TransactionReceipt const& _t)
{
Json::Value res;
res["stateRoot"] = toJS(_t.stateRoot());
res["gasUsed"] = toJS(_t.gasUsed());
res["bloom"] = toJS(_t.bloom());
res["log"] = dev::toJson(_t.log());
return res;
}
Json::Value toJson(dev::eth::Transaction const& _t)
{
Json::Value res;
res["to"] = _t.isCreation() ? Json::Value() : toJS(_t.to());
res["from"] = toJS(_t.from());
res["gas"] = toJS(_t.gas());
res["gasPrice"] = toJS(_t.gasPrice());
res["value"] = toJS(_t.value());
res["data"] = toJS(_t.data(), 32);
res["nonce"] = toJS(_t.nonce());
res["hash"] = toJS(_t.sha3(WithSignature));
res["sighash"] = toJS(_t.sha3(WithoutSignature));
res["r"] = toJS(_t.signature().r);
res["s"] = toJS(_t.signature().s);
res["v"] = toJS(_t.signature().v);
return res;
}
Json::Value toJson(dev::eth::LocalisedLogEntry const& _e)
{
Json::Value res;
if (_e.isSpecial)
res = toJS(_e.special);
else
{
res = toJson(static_cast<dev::eth::LogEntry const&>(_e));
if (_e.mined)
{
res["type"] = "mined";
res["blockNumber"] = _e.blockNumber;
res["blockHash"] = toJS(_e.blockHash);
res["logIndex"] = _e.logIndex;
res["transactionHash"] = toJS(_e.transactionHash);
res["transactionIndex"] = _e.transactionIndex;
}
else
{
res["type"] = "pending";
res["blockNumber"] = Json::Value(Json::nullValue);
res["blockHash"] = Json::Value(Json::nullValue);
res["logIndex"] = Json::Value(Json::nullValue);
res["transactionHash"] = Json::Value(Json::nullValue);
res["transactionIndex"] = Json::Value(Json::nullValue);
}
}
return res;
}
Json::Value toJson(dev::eth::LogEntry const& _e)
{
Json::Value res;
res["data"] = toJS(_e.data);
res["address"] = toJS(_e.address);
res["topics"] = Json::Value(Json::arrayValue);
for (auto const& t: _e.topics)
res["topics"].append(toJS(t));
return res;
}
TransactionSkeleton toTransactionSkeleton(Json::Value const& _json)
{
TransactionSkeleton ret;
if (!_json.isObject() || _json.empty())
return ret;
if (!_json["from"].empty())
ret.from = jsToAddress(_json["from"].asString());
if (!_json["to"].empty() && _json["to"].asString() != "0x")
ret.to = jsToAddress(_json["to"].asString());
else
ret.creation = true;
if (!_json["value"].empty())
ret.value = jsToU256(_json["value"].asString());
if (!_json["gas"].empty())
ret.gas = jsToU256(_json["gas"].asString());
if (!_json["gasPrice"].empty())
ret.gasPrice = jsToU256(_json["gasPrice"].asString());
if (!_json["data"].empty()) // ethereum.js has preconstructed the data array
ret.data = jsToBytes(_json["data"].asString());
if (!_json["code"].empty())
ret.data = jsToBytes(_json["code"].asString());
return ret;
}
dev::eth::LogFilter toLogFilter(Json::Value const& _json)
{
dev::eth::LogFilter filter;
if (!_json.isObject() || _json.empty())
return filter;
// check only !empty. it should throw exceptions if input params are incorrect
if (!_json["fromBlock"].empty())
filter.withEarliest(jsToFixed<32>(_json["fromBlock"].asString()));
if (!_json["toBlock"].empty())
filter.withLatest(jsToFixed<32>(_json["toBlock"].asString()));
if (!_json["address"].empty())
{
if (_json["address"].isArray())
for (auto i : _json["address"])
filter.address(jsToAddress(i.asString()));
else
filter.address(jsToAddress(_json["address"].asString()));
}
if (!_json["topics"].empty())
for (unsigned i = 0; i < _json["topics"].size(); i++)
{
if (_json["topics"][i].isArray())
{
for (auto t: _json["topics"][i])
if (!t.isNull())
filter.topic(i, jsToFixed<32>(t.asString()));
}
else if (!_json["topics"][i].isNull()) // if it is anything else then string, it should and will fail
filter.topic(i, jsToFixed<32>(_json["topics"][i].asString()));
}
return filter;
}
// TODO: this should be removed once we decide to remove backward compatibility with old log filters
dev::eth::LogFilter toLogFilter(Json::Value const& _json, Interface const& _client) // commented to avoid warning. Uncomment once in use @ PoC-7.
{
dev::eth::LogFilter filter;
if (!_json.isObject() || _json.empty())
return filter;
// check only !empty. it should throw exceptions if input params are incorrect
if (!_json["fromBlock"].empty())
filter.withEarliest(_client.hashFromNumber(jsToBlockNumber(_json["fromBlock"].asString())));
if (!_json["toBlock"].empty())
filter.withLatest(_client.hashFromNumber(jsToBlockNumber(_json["toBlock"].asString())));
if (!_json["address"].empty())
{
if (_json["address"].isArray())
for (auto i : _json["address"])
filter.address(jsToAddress(i.asString()));
else
filter.address(jsToAddress(_json["address"].asString()));
}
if (!_json["topics"].empty())
for (unsigned i = 0; i < _json["topics"].size(); i++)
{
if (_json["topics"][i].isArray())
{
for (auto t: _json["topics"][i])
if (!t.isNull())
filter.topic(i, jsToFixed<32>(t.asString()));
}
else if (!_json["topics"][i].isNull()) // if it is anything else then string, it should and will fail
filter.topic(i, jsToFixed<32>(_json["topics"][i].asString()));
}
return filter;
}
}
// ////////////////////////////////////////////////////////////////////////////////////
// shh
// ////////////////////////////////////////////////////////////////////////////////////
namespace shh
{
Json::Value toJson(h256 const& _h, shh::Envelope const& _e, shh::Message const& _m)
{
Json::Value res;
res["hash"] = toJS(_h);
res["expiry"] = toJS(_e.expiry());
res["sent"] = toJS(_e.sent());
res["ttl"] = toJS(_e.ttl());
res["workProved"] = toJS(_e.workProved());
res["topics"] = Json::Value(Json::arrayValue);
for (auto const& t: _e.topic())
res["topics"].append(toJS(t));
res["payload"] = toJS(_m.payload());
res["from"] = toJS(_m.from());
res["to"] = toJS(_m.to());
return res;
}
shh::Message toMessage(Json::Value const& _json)
{
shh::Message ret;
if (!_json["from"].empty())
ret.setFrom(jsToPublic(_json["from"].asString()));
if (!_json["to"].empty())
ret.setTo(jsToPublic(_json["to"].asString()));
if (!_json["payload"].empty())
ret.setPayload(jsToBytes(_json["payload"].asString()));
return ret;
}
shh::Envelope toSealed(Json::Value const& _json, shh::Message const& _m, Secret _from)
{
unsigned ttl = 50;
unsigned workToProve = 50;
shh::BuildTopic bt;
if (!_json["ttl"].empty())
ttl = jsToInt(_json["ttl"].asString());
if (!_json["workToProve"].empty())
workToProve = jsToInt(_json["workToProve"].asString());
if (!_json["topics"].empty())
for (auto i: _json["topics"])
{
if (i.isArray())
{
for (auto j: i)
if (!j.isNull())
bt.shift(jsToBytes(j.asString()));
}
else if (!i.isNull()) // if it is anything else then string, it should and will fail
bt.shift(jsToBytes(i.asString()));
}
return _m.seal(_from, bt, ttl, workToProve);
}
pair<shh::Topics, Public> toWatch(Json::Value const& _json)
{
shh::BuildTopic bt;
Public to;
if (!_json["to"].empty())
to = jsToPublic(_json["to"].asString());
if (!_json["topics"].empty())
for (auto i: _json["topics"])
bt.shift(jsToBytes(i.asString()));
return make_pair(bt, to);
}
}
}

104
libweb3jsonrpc/JsonHelper.h

@ -0,0 +1,104 @@
/*
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 JsonHelper.h
* @authors:
* Gav Wood <i@gavwood.com>
* @date 2015
*/
#pragma once
#include <json/json.h>
#include <libethcore/Common.h>
#include <libethereum/LogFilter.h>
#include <libwhisper/Message.h>
namespace dev
{
Json::Value toJson(std::map<u256, u256> const& _storage);
Json::Value toJson(std::unordered_map<u256, u256> const& _storage);
namespace p2p
{
Json::Value toJson(PeerSessionInfo const& _p);
}
namespace eth
{
class Transaction;
class BlockDetails;
class Interface;
using Transactions = std::vector<Transaction>;
using UncleHashes = h256s;
using TransactionHashes = h256s;
Json::Value toJson(BlockInfo const& _bi);
Json::Value toJson(Transaction const& _t, std::pair<h256, unsigned> _location, BlockNumber _blockNumber);
Json::Value toJson(BlockInfo const& _bi, BlockDetails const& _bd, UncleHashes const& _us, Transactions const& _ts);
Json::Value toJson(BlockInfo const& _bi, BlockDetails const& _bd, UncleHashes const& _us, TransactionHashes const& _ts);
Json::Value toJson(TransactionSkeleton const& _t);
Json::Value toJson(Transaction const& _t);
Json::Value toJson(TransactionReceipt const& _t);
Json::Value toJson(LocalisedLogEntry const& _e);
Json::Value toJson(LogEntry const& _e);
TransactionSkeleton toTransactionSkeleton(Json::Value const& _json);
LogFilter toLogFilter(Json::Value const& _json);
LogFilter toLogFilter(Json::Value const& _json, Interface const& _client); // commented to avoid warning. Uncomment once in use @ PoC-7.
}
namespace shh
{
Json::Value toJson(h256 const& _h, Envelope const& _e, Message const& _m);
Message toMessage(Json::Value const& _json);
Envelope toSealed(Json::Value const& _json, Message const& _m, Secret _from);
std::pair<Topics, Public> toWatch(Json::Value const& _json);
}
template <class T>
Json::Value toJson(std::vector<T> const& _es)
{
Json::Value res(Json::arrayValue);
for (auto const& e: _es)
res.append(toJson(e));
return res;
}
template <class T>
Json::Value toJson(std::unordered_set<T> const& _es)
{
Json::Value res(Json::arrayValue);
for (auto const& e: _es)
res.append(toJson(e));
return res;
}
template <class T>
Json::Value toJson(std::set<T> const& _es)
{
Json::Value res(Json::arrayValue);
for (auto const& e: _es)
res.append(toJson(e));
return res;
}
}

268
libweb3jsonrpc/WebThreeStubServer.cpp

@ -21,21 +21,39 @@
* @date 2014 * @date 2014
*/ */
#include "WebThreeStubServer.h"
// Make sure boost/asio.hpp is included before windows.h. // Make sure boost/asio.hpp is included before windows.h.
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <libwebthree/WebThree.h>
#include <libdevcore/FileSystem.h> #include <libdevcore/FileSystem.h>
#include "WebThreeStubServer.h" #include <libdevcore/CommonJS.h>
#include <libethcore/KeyManager.h>
#include <libethereum/Executive.h>
#include <libwebthree/WebThree.h>
#include "JsonHelper.h"
using namespace std; using namespace std;
using namespace dev; using namespace dev;
using namespace dev::eth; using namespace dev::eth;
WebThreeStubServer::WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, WebThreeDirect& _web3, shared_ptr<AccountHolder> const& _ethAccounts, std::vector<dev::KeyPair> const& _shhAccounts): bool isHex(std::string const& _s)
{
unsigned i = (_s.size() >= 2 && _s.substr(0, 2) == "0x") ? 2 : 0;
for (; i < _s.size(); ++i)
if (fromHex(_s[i], WhenError::DontThrow) == -1)
return false;
return true;
}
template <class T> bool isHash(std::string const& _hash)
{
return (_hash.size() == T::size * 2 || (_hash.size() == T::size * 2 + 2 && _hash.substr(0, 2) == "0x")) && isHex(_hash);
}
WebThreeStubServer::WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, WebThreeDirect& _web3, shared_ptr<AccountHolder> const& _ethAccounts, std::vector<dev::KeyPair> const& _shhAccounts, KeyManager& _keyMan, dev::eth::TrivialGasPricer& _gp):
WebThreeStubServerBase(_conn, _ethAccounts, _shhAccounts), WebThreeStubServerBase(_conn, _ethAccounts, _shhAccounts),
m_web3(_web3) m_web3(_web3),
m_keyMan(_keyMan),
m_gp(_gp)
{ {
auto path = getDataDir() + "/.web3"; auto path = getDataDir() + "/.web3";
boost::filesystem::create_directories(path); boost::filesystem::create_directories(path);
@ -44,6 +62,244 @@ WebThreeStubServer::WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn,
ldb::DB::Open(o, path, &m_db); ldb::DB::Open(o, path, &m_db);
} }
std::string WebThreeStubServer::newSession(SessionPermissions const& _p)
{
std::string s = toBase64(h64::random().ref());
m_sessions[s] = _p;
return s;
}
bool WebThreeStubServer::eth_notePassword(string const& _password)
{
m_keyMan.notePassword(_password);
return true;
}
#define ADMIN requires(_session, Priviledge::Admin)
Json::Value WebThreeStubServer::admin_eth_blockQueueStatus(string const& _session)
{
ADMIN;
Json::Value ret;
BlockQueueStatus bqs = m_web3.ethereum()->blockQueue().status();
ret["importing"] = (int)bqs.importing;
ret["verified"] = (int)bqs.verified;
ret["verifying"] = (int)bqs.verifying;
ret["unverified"] = (int)bqs.unverified;
ret["future"] = (int)bqs.future;
ret["unknown"] = (int)bqs.unknown;
ret["bad"] = (int)bqs.bad;
return ret;
}
bool WebThreeStubServer::admin_eth_setAskPrice(std::string const& _wei, std::string const& _session)
{
ADMIN;
m_gp.setAsk(jsToU256(_wei));
return true;
}
bool WebThreeStubServer::admin_eth_setBidPrice(std::string const& _wei, std::string const& _session)
{
ADMIN;
m_gp.setBid(jsToU256(_wei));
return true;
}
dev::eth::CanonBlockChain const& WebThreeStubServer::bc() const
{
return m_web3.ethereum()->blockChain();
}
dev::eth::BlockQueue const& WebThreeStubServer::bq() const
{
return m_web3.ethereum()->blockQueue();
}
Json::Value WebThreeStubServer::admin_eth_findBlock(std::string const& _blockHash, std::string const& _session)
{
ADMIN;
h256 h(_blockHash);
if (bc().isKnown(h))
return toJson(bc().info(h));
switch(bq().blockStatus(h))
{
case QueueStatus::Ready:
return "ready";
case QueueStatus::Importing:
return "importing";
case QueueStatus::UnknownParent:
return "unknown parent";
case QueueStatus::Bad:
return "bad";
default:
return "unknown";
}
}
std::string WebThreeStubServer::admin_eth_blockQueueFirstUnknown(std::string const& _session)
{
ADMIN;
return bq().firstUnknown().hex();
}
bool WebThreeStubServer::admin_eth_blockQueueRetryUnknown(std::string const& _session)
{
ADMIN;
m_web3.ethereum()->retryUnknown();
return true;
}
Json::Value WebThreeStubServer::admin_eth_allAccounts(std::string const& _session)
{
ADMIN;
Json::Value ret;
u256 total = 0;
u256 pendingtotal = 0;
Address beneficiary;
for (auto const& i: m_keyMan.accountDetails())
{
auto pending = m_web3.ethereum()->balanceAt(i.first, PendingBlock);
auto latest = m_web3.ethereum()->balanceAt(i.first, LatestBlock);
Json::Value a;
if (i.first == beneficiary)
a["beneficiary"] = true;
a["address"] = toJS(i.first);
a["balance"] = toJS(latest);
a["nicebalance"] = formatBalance(latest);
a["pending"] = toJS(pending);
a["nicepending"] = formatBalance(pending);
ret["accounts"][i.second.first] = a;
total += latest;
pendingtotal += pending;
}
ret["total"] = toJS(total);
ret["nicetotal"] = formatBalance(total);
ret["pendingtotal"] = toJS(pendingtotal);
ret["nicependingtotal"] = formatBalance(pendingtotal);
return ret;
}
Json::Value WebThreeStubServer::admin_eth_newAccount(Json::Value const& _info, std::string const& _session)
{
ADMIN;
if (!_info.isMember("name"))
throw jsonrpc::JsonRpcException("No member found: name");
string name = _info["name"].asString();
auto s = Secret::random();
h128 uuid;
if (_info.isMember("password"))
{
string password = _info["password"].asString();
string hint = _info["passwordHint"].asString();
uuid = m_keyMan.import(s, name, password, hint);
}
else
uuid = m_keyMan.import(s, name);
Json::Value ret;
ret["account"] = toJS(toAddress(s));
ret["uuid"] = toUUID(uuid);
return ret;
}
bool WebThreeStubServer::admin_eth_setMiningBenefactor(std::string const& _uuidOrAddress, std::string const& _session)
{
ADMIN;
(void)_uuidOrAddress;
return true;
}
Json::Value WebThreeStubServer::admin_eth_inspect(std::string const& _address, std::string const& _session)
{
ADMIN;
if (!isHash<Address>(_address))
throw jsonrpc::JsonRpcException("Invalid address given.");
Json::Value ret;
auto h = Address(fromHex(_address));
ret["storage"] = toJson(m_web3.ethereum()->storageAt(h, PendingBlock));
ret["balance"] = toJS(m_web3.ethereum()->balanceAt(h, PendingBlock));
ret["nonce"] = toJS(m_web3.ethereum()->countAt(h, PendingBlock));
ret["code"] = toJS(m_web3.ethereum()->codeAt(h, PendingBlock));
return ret;
}
h256 WebThreeStubServer::blockHash(std::string const& _blockNumberOrHash) const
{
if (isHash<h256>(_blockNumberOrHash))
return h256(_blockNumberOrHash.substr(_blockNumberOrHash.size() - 64, 64));
try
{
return bc().numberHash(stoul(_blockNumberOrHash));
}
catch (...)
{
throw jsonrpc::JsonRpcException("Invalid argument");
}
}
Json::Value WebThreeStubServer::admin_eth_reprocess(std::string const& _blockNumberOrHash, std::string const& _session)
{
ADMIN;
Json::Value ret;
PopulationStatistics ps;
m_web3.ethereum()->state(blockHash(_blockNumberOrHash), &ps);
ret["enact"] = ps.enact;
ret["verify"] = ps.verify;
ret["total"] = ps.verify + ps.enact;
return ret;
}
Json::Value WebThreeStubServer::admin_eth_vmTrace(std::string const& _blockNumberOrHash, int _txIndex, std::string const& _session)
{
ADMIN;
Json::Value ret;
auto c = m_web3.ethereum();
State state = c->state(_txIndex + 1, blockHash(_blockNumberOrHash));
if (_txIndex < 0)
throw jsonrpc::JsonRpcException("Negative index");
if ((unsigned)_txIndex < state.pending().size())
{
Executive e(state, bc(), 0);
Transaction t = state.pending()[_txIndex];
state = state.fromPending(_txIndex);
try
{
StandardTrace st;
st.setShowMnemonics();
e.initialize(t);
if (!e.execute())
e.go(st.onOp());
e.finalize();
Json::Reader().parse(st.json(), ret);
}
catch(Exception const& _e)
{
cwarn << diagnostic_information(_e);
}
}
return ret;
}
Json::Value WebThreeStubServer::admin_eth_getReceiptByHashAndIndex(std::string const& _blockNumberOrHash, int _txIndex, std::string const& _session)
{
ADMIN;
if (_txIndex < 0)
throw jsonrpc::JsonRpcException("Negative index");
auto h = blockHash(_blockNumberOrHash);
if (!bc().isKnown(h))
throw jsonrpc::JsonRpcException("Invalid/unknown block.");
auto rs = bc().receipts(h);
if ((unsigned)_txIndex >= rs.receipts.size())
throw jsonrpc::JsonRpcException("Index too large.");
return toJson(rs.receipts[_txIndex]);
}
std::string WebThreeStubServer::web3_clientVersion() std::string WebThreeStubServer::web3_clientVersion()
{ {
return m_web3.clientVersion(); return m_web3.clientVersion();

47
libweb3jsonrpc/WebThreeStubServer.h

@ -32,20 +32,37 @@
namespace dev namespace dev
{ {
class WebThreeDirect; class WebThreeDirect;
namespace eth
{
class KeyManager;
class TrivialGasPricer;
class CanonBlockChain;
class BlockQueue;
} }
struct SessionPermissions
{
std::unordered_set<Priviledge> priviledges;
};
/** /**
* @brief JSON-RPC api implementation for WebThreeDirect * @brief JSON-RPC api implementation for WebThreeDirect
*/ */
class WebThreeStubServer: public dev::WebThreeStubServerBase, public dev::WebThreeStubDatabaseFace class WebThreeStubServer: public dev::WebThreeStubServerBase, public dev::WebThreeStubDatabaseFace
{ {
public: public:
WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, dev::WebThreeDirect& _web3, std::shared_ptr<dev::eth::AccountHolder> const& _ethAccounts, std::vector<dev::KeyPair> const& _shhAccounts); WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, dev::WebThreeDirect& _web3, std::shared_ptr<dev::eth::AccountHolder> const& _ethAccounts, std::vector<dev::KeyPair> const& _shhAccounts, dev::eth::KeyManager& _keyMan, dev::eth::TrivialGasPricer& _gp);
virtual std::string web3_clientVersion() override; virtual std::string web3_clientVersion() override;
std::string newSession(SessionPermissions const& _p);
void addSession(std::string const& _session, SessionPermissions const& _p) { m_sessions[_session] = _p; }
private: private:
virtual bool hasPriviledgeLevel(std::string const& _session, Priviledge _l) const override { auto it = m_sessions.find(_session); return it != m_sessions.end() && it->second.priviledges.count(_l); }
virtual dev::eth::Interface* client() override; virtual dev::eth::Interface* client() override;
virtual std::shared_ptr<dev::shh::Interface> face() override; virtual std::shared_ptr<dev::shh::Interface> face() override;
virtual dev::WebThreeNetworkFace* network() override; virtual dev::WebThreeNetworkFace* network() override;
@ -54,9 +71,37 @@ private:
virtual std::string get(std::string const& _name, std::string const& _key) override; virtual std::string get(std::string const& _name, std::string const& _key) override;
virtual void put(std::string const& _name, std::string const& _key, std::string const& _value) override; virtual void put(std::string const& _name, std::string const& _key, std::string const& _value) override;
virtual bool eth_notePassword(std::string const& _password) override;
virtual Json::Value admin_eth_blockQueueStatus(std::string const& _session) override;
virtual bool admin_eth_setAskPrice(std::string const& _wei, std::string const& _session) override;
virtual bool admin_eth_setBidPrice(std::string const& _wei, std::string const& _session) override;
virtual Json::Value admin_eth_findBlock(std::string const& _blockHash, std::string const& _session) override;
virtual std::string admin_eth_blockQueueFirstUnknown(std::string const& _session) override;
virtual bool admin_eth_blockQueueRetryUnknown(std::string const& _session) override;
virtual Json::Value admin_eth_allAccounts(std::string const& _session) override;
virtual Json::Value admin_eth_newAccount(const Json::Value& _info, std::string const& _session) override;
virtual bool admin_eth_setMiningBenefactor(std::string const& _uuidOrAddress, std::string const& _session) override;
virtual Json::Value admin_eth_inspect(std::string const& _address, std::string const& _session) override;
virtual Json::Value admin_eth_reprocess(std::string const& _blockNumberOrHash, std::string const& _session) override;
virtual Json::Value admin_eth_vmTrace(std::string const& _blockNumberOrHash, int _txIndex, std::string const& _session) override;
virtual Json::Value admin_eth_getReceiptByHashAndIndex(std::string const& _blockNumberOrHash, int _txIndex, std::string const& _session) override;
private: private:
h256 blockHash(std::string const& _blockNumberOrHash) const;
dev::eth::CanonBlockChain const& bc() const;
dev::eth::BlockQueue const& bq() const;
dev::WebThreeDirect& m_web3; dev::WebThreeDirect& m_web3;
dev::eth::KeyManager& m_keyMan;
dev::eth::TrivialGasPricer& m_gp;
leveldb::ReadOptions m_readOptions; leveldb::ReadOptions m_readOptions;
leveldb::WriteOptions m_writeOptions; leveldb::WriteOptions m_writeOptions;
leveldb::DB* m_db; leveldb::DB* m_db;
std::unordered_map<std::string, SessionPermissions> m_sessions;
}; };
}

401
libweb3jsonrpc/WebThreeStubServerBase.cpp

@ -21,6 +21,8 @@
* @date 2014 * @date 2014
*/ */
#include "WebThreeStubServerBase.h"
// Make sure boost/asio.hpp is included before windows.h. // Make sure boost/asio.hpp is included before windows.h.
#include <boost/asio.hpp> #include <boost/asio.hpp>
@ -41,13 +43,13 @@
#if ETH_SERPENT || !ETH_TRUE #if ETH_SERPENT || !ETH_TRUE
#include <libserpent/funcs.h> #include <libserpent/funcs.h>
#endif #endif
#include "WebThreeStubServerBase.h"
#include "AccountHolder.h" #include "AccountHolder.h"
#include "JsonHelper.h"
using namespace std; using namespace std;
using namespace jsonrpc; using namespace jsonrpc;
using namespace dev; using namespace dev;
using namespace dev::eth; using namespace eth;
using namespace shh;
#if ETH_DEBUG #if ETH_DEBUG
const unsigned dev::SensibleHttpThreads = 1; const unsigned dev::SensibleHttpThreads = 1;
@ -56,302 +58,6 @@ const unsigned dev::SensibleHttpThreads = 4;
#endif #endif
const unsigned dev::SensibleHttpPort = 8545; const unsigned dev::SensibleHttpPort = 8545;
static Json::Value toJson(dev::eth::BlockInfo const& _bi)
{
Json::Value res;
if (_bi)
{
res["hash"] = toJS(_bi.hash());
res["parentHash"] = toJS(_bi.parentHash);
res["sha3Uncles"] = toJS(_bi.sha3Uncles);
res["miner"] = toJS(_bi.coinbaseAddress);
res["stateRoot"] = toJS(_bi.stateRoot);
res["transactionsRoot"] = toJS(_bi.transactionsRoot);
res["difficulty"] = toJS(_bi.difficulty);
res["number"] = toJS(_bi.number);
res["gasUsed"] = toJS(_bi.gasUsed);
res["gasLimit"] = toJS(_bi.gasLimit);
res["timestamp"] = toJS(_bi.timestamp);
res["extraData"] = toJS(_bi.extraData);
res["nonce"] = toJS(_bi.nonce);
res["logsBloom"] = toJS(_bi.logBloom);
}
return res;
}
static Json::Value toJson(dev::eth::Transaction const& _t, std::pair<h256, unsigned> _location, BlockNumber _blockNumber)
{
Json::Value res;
if (_t)
{
res["hash"] = toJS(_t.sha3());
res["input"] = toJS(_t.data());
res["to"] = _t.isCreation() ? Json::Value() : toJS(_t.receiveAddress());
res["from"] = toJS(_t.safeSender());
res["gas"] = toJS(_t.gas());
res["gasPrice"] = toJS(_t.gasPrice());
res["nonce"] = toJS(_t.nonce());
res["value"] = toJS(_t.value());
res["blockHash"] = toJS(_location.first);
res["transactionIndex"] = toJS(_location.second);
res["blockNumber"] = toJS(_blockNumber);
}
return res;
}
static Json::Value toJson(dev::eth::BlockInfo const& _bi, BlockDetails const& _bd, UncleHashes const& _us, Transactions const& _ts)
{
Json::Value res = toJson(_bi);
if (_bi)
{
res["totalDifficulty"] = toJS(_bd.totalDifficulty);
res["uncles"] = Json::Value(Json::arrayValue);
for (h256 h: _us)
res["uncles"].append(toJS(h));
res["transactions"] = Json::Value(Json::arrayValue);
for (unsigned i = 0; i < _ts.size(); i++)
res["transactions"].append(toJson(_ts[i], std::make_pair(_bi.hash(), i), (BlockNumber)_bi.number));
}
return res;
}
static Json::Value toJson(dev::eth::BlockInfo const& _bi, BlockDetails const& _bd, UncleHashes const& _us, TransactionHashes const& _ts)
{
Json::Value res = toJson(_bi);
if (_bi)
{
res["totalDifficulty"] = toJS(_bd.totalDifficulty);
res["uncles"] = Json::Value(Json::arrayValue);
for (h256 h: _us)
res["uncles"].append(toJS(h));
res["transactions"] = Json::Value(Json::arrayValue);
for (h256 const& t: _ts)
res["transactions"].append(toJS(t));
}
return res;
}
static Json::Value toJson(dev::eth::TransactionSkeleton const& _t)
{
Json::Value res;
res["to"] = _t.creation ? Json::Value() : toJS(_t.to);
res["from"] = toJS(_t.from);
res["gas"] = toJS(_t.gas);
res["gasPrice"] = toJS(_t.gasPrice);
res["value"] = toJS(_t.value);
res["data"] = toJS(_t.data, 32);
return res;
}
static Json::Value toJson(dev::eth::Transaction const& _t)
{
Json::Value res;
res["to"] = _t.isCreation() ? Json::Value() : toJS(_t.to());
res["from"] = toJS(_t.from());
res["gas"] = toJS(_t.gas());
res["gasPrice"] = toJS(_t.gasPrice());
res["value"] = toJS(_t.value());
res["data"] = toJS(_t.data(), 32);
res["nonce"] = toJS(_t.nonce());
res["hash"] = toJS(_t.sha3(WithSignature));
res["sighash"] = toJS(_t.sha3(WithoutSignature));
res["r"] = toJS(_t.signature().r);
res["s"] = toJS(_t.signature().s);
res["v"] = toJS(_t.signature().v);
return res;
}
static Json::Value toJson(dev::eth::LocalisedLogEntry const& _e)
{
Json::Value res;
if (_e.topics.size() > 0)
{
res["data"] = toJS(_e.data);
res["address"] = toJS(_e.address);
res["topics"] = Json::Value(Json::arrayValue);
for (auto const& t: _e.topics)
res["topics"].append(toJS(t));
if (_e.mined)
{
res["type"] = "mined";
res["blockNumber"] = _e.blockNumber;
res["blockHash"] = toJS(_e.blockHash);
res["logIndex"] = _e.logIndex;
res["transactionHash"] = toJS(_e.transactionHash);
res["transactionIndex"] = _e.transactionIndex;
}
else
{
res["type"] = "pending";
res["blockNumber"] = Json::Value(Json::nullValue);
res["blockHash"] = Json::Value(Json::nullValue);
res["logIndex"] = Json::Value(Json::nullValue);
res["transactionHash"] = Json::Value(Json::nullValue);
res["transactionIndex"] = Json::Value(Json::nullValue);
}
} else {
res = toJS(_e.special);
}
return res;
}
static Json::Value toJson(dev::eth::LocalisedLogEntries const& _es)
{
Json::Value res(Json::arrayValue);
for (dev::eth::LocalisedLogEntry const& e: _es)
res.append(toJson(e));
return res;
}
static Json::Value toJson(map<u256, u256> const& _storage)
{
Json::Value res(Json::objectValue);
for (auto i: _storage)
res[toJS(i.first)] = toJS(i.second);
return res;
}
static dev::eth::LogFilter toLogFilter(Json::Value const& _json)
{
dev::eth::LogFilter filter;
if (!_json.isObject() || _json.empty())
return filter;
// check only !empty. it should throw exceptions if input params are incorrect
if (!_json["fromBlock"].empty())
filter.withEarliest(jsToFixed<32>(_json["fromBlock"].asString()));
if (!_json["toBlock"].empty())
filter.withLatest(jsToFixed<32>(_json["toBlock"].asString()));
if (!_json["address"].empty())
{
if (_json["address"].isArray())
for (auto i : _json["address"])
filter.address(jsToAddress(i.asString()));
else
filter.address(jsToAddress(_json["address"].asString()));
}
if (!_json["topics"].empty())
for (unsigned i = 0; i < _json["topics"].size(); i++)
{
if (_json["topics"][i].isArray())
{
for (auto t: _json["topics"][i])
if (!t.isNull())
filter.topic(i, jsToFixed<32>(t.asString()));
}
else if (!_json["topics"][i].isNull()) // if it is anything else then string, it should and will fail
filter.topic(i, jsToFixed<32>(_json["topics"][i].asString()));
}
return filter;
}
// TODO: this should be removed once we decide to remove backward compatibility with old log filters
static dev::eth::LogFilter toLogFilter(Json::Value const& _json, Interface const& _client) // commented to avoid warning. Uncomment once in use @ PoC-7.
{
dev::eth::LogFilter filter;
if (!_json.isObject() || _json.empty())
return filter;
// check only !empty. it should throw exceptions if input params are incorrect
if (!_json["fromBlock"].empty())
filter.withEarliest(_client.hashFromNumber(jsToBlockNumber(_json["fromBlock"].asString())));
if (!_json["toBlock"].empty())
filter.withLatest(_client.hashFromNumber(jsToBlockNumber(_json["toBlock"].asString())));
if (!_json["address"].empty())
{
if (_json["address"].isArray())
for (auto i : _json["address"])
filter.address(jsToAddress(i.asString()));
else
filter.address(jsToAddress(_json["address"].asString()));
}
if (!_json["topics"].empty())
for (unsigned i = 0; i < _json["topics"].size(); i++)
{
if (_json["topics"][i].isArray())
{
for (auto t: _json["topics"][i])
if (!t.isNull())
filter.topic(i, jsToFixed<32>(t.asString()));
}
else if (!_json["topics"][i].isNull()) // if it is anything else then string, it should and will fail
filter.topic(i, jsToFixed<32>(_json["topics"][i].asString()));
}
return filter;
}
static shh::Message toMessage(Json::Value const& _json)
{
shh::Message ret;
if (!_json["from"].empty())
ret.setFrom(jsToPublic(_json["from"].asString()));
if (!_json["to"].empty())
ret.setTo(jsToPublic(_json["to"].asString()));
if (!_json["payload"].empty())
ret.setPayload(jsToBytes(_json["payload"].asString()));
return ret;
}
static shh::Envelope toSealed(Json::Value const& _json, shh::Message const& _m, Secret _from)
{
unsigned ttl = 50;
unsigned workToProve = 50;
shh::BuildTopic bt;
if (!_json["ttl"].empty())
ttl = jsToInt(_json["ttl"].asString());
if (!_json["workToProve"].empty())
workToProve = jsToInt(_json["workToProve"].asString());
if (!_json["topics"].empty())
for (auto i: _json["topics"])
{
if (i.isArray())
{
for (auto j: i)
if (!j.isNull())
bt.shift(jsToBytes(j.asString()));
}
else if (!i.isNull()) // if it is anything else then string, it should and will fail
bt.shift(jsToBytes(i.asString()));
}
return _m.seal(_from, bt, ttl, workToProve);
}
static pair<shh::Topics, Public> toWatch(Json::Value const& _json)
{
shh::BuildTopic bt;
Public to;
if (!_json["to"].empty())
to = jsToPublic(_json["to"].asString());
if (!_json["topics"].empty())
for (auto i: _json["topics"])
bt.shift(jsToBytes(i.asString()));
return make_pair(bt, to);
}
static Json::Value toJson(h256 const& _h, shh::Envelope const& _e, shh::Message const& _m)
{
Json::Value res;
res["hash"] = toJS(_h);
res["expiry"] = toJS(_e.expiry());
res["sent"] = toJS(_e.sent());
res["ttl"] = toJS(_e.ttl());
res["workProved"] = toJS(_e.workProved());
res["topics"] = Json::Value(Json::arrayValue);
for (auto const& t: _e.topic())
res["topics"].append(toJS(t));
res["payload"] = toJS(_m.payload());
res["from"] = toJS(_m.from());
res["to"] = toJS(_m.to());
return res;
}
WebThreeStubServerBase::WebThreeStubServerBase(AbstractServerConnector& _conn, std::shared_ptr<dev::eth::AccountHolder> const& _ethAccounts, vector<dev::KeyPair> const& _sshAccounts): WebThreeStubServerBase::WebThreeStubServerBase(AbstractServerConnector& _conn, std::shared_ptr<dev::eth::AccountHolder> const& _ethAccounts, vector<dev::KeyPair> const& _sshAccounts):
AbstractWebThreeStubServer(_conn), AbstractWebThreeStubServer(_conn),
m_ethAccounts(_ethAccounts) m_ethAccounts(_ethAccounts)
@ -468,7 +174,6 @@ string WebThreeStubServerBase::eth_getBlockTransactionCountByHash(string const&
} }
} }
string WebThreeStubServerBase::eth_getBlockTransactionCountByNumber(string const& _blockNumber) string WebThreeStubServerBase::eth_getBlockTransactionCountByNumber(string const& _blockNumber)
{ {
try try
@ -517,42 +222,12 @@ string WebThreeStubServerBase::eth_getCode(string const& _address, string const&
} }
} }
static TransactionSkeleton toTransaction(Json::Value const& _json)
{
TransactionSkeleton ret;
if (!_json.isObject() || _json.empty())
return ret;
if (!_json["from"].empty())
ret.from = jsToAddress(_json["from"].asString());
if (!_json["to"].empty() && _json["to"].asString() != "0x")
ret.to = jsToAddress(_json["to"].asString());
else
ret.creation = true;
if (!_json["value"].empty())
ret.value = jsToU256(_json["value"].asString());
if (!_json["gas"].empty())
ret.gas = jsToU256(_json["gas"].asString());
if (!_json["gasPrice"].empty())
ret.gasPrice = jsToU256(_json["gasPrice"].asString());
if (!_json["data"].empty()) // ethereum.js has preconstructed the data array
ret.data = jsToBytes(_json["data"].asString());
if (!_json["code"].empty())
ret.data = jsToBytes(_json["code"].asString());
return ret;
}
string WebThreeStubServerBase::eth_sendTransaction(Json::Value const& _json) string WebThreeStubServerBase::eth_sendTransaction(Json::Value const& _json)
{ {
try try
{ {
string ret; string ret;
TransactionSkeleton t = toTransaction(_json); TransactionSkeleton t = toTransactionSkeleton(_json);
if (!t.from) if (!t.from)
t.from = m_ethAccounts->defaultTransactAccount(); t.from = m_ethAccounts->defaultTransactAccount();
@ -578,7 +253,7 @@ string WebThreeStubServerBase::eth_signTransaction(Json::Value const& _json)
try try
{ {
string ret; string ret;
TransactionSkeleton t = toTransaction(_json); TransactionSkeleton t = toTransactionSkeleton(_json);
if (!t.from) if (!t.from)
t.from = m_ethAccounts->defaultTransactAccount(); t.from = m_ethAccounts->defaultTransactAccount();
@ -627,7 +302,7 @@ string WebThreeStubServerBase::eth_call(Json::Value const& _json, string const&
{ {
try try
{ {
TransactionSkeleton t = toTransaction(_json); TransactionSkeleton t = toTransactionSkeleton(_json);
if (!t.from) if (!t.from)
t.from = m_ethAccounts->defaultTransactAccount(); t.from = m_ethAccounts->defaultTransactAccount();
// if (!m_accounts->isRealAccount(t.from)) // if (!m_accounts->isRealAccount(t.from))
@ -798,6 +473,64 @@ string WebThreeStubServerBase::eth_compileSerpent(string const& _source)
return res; return res;
} }
#define ADMIN requires(_session, Priviledge::Admin)
bool WebThreeStubServerBase::admin_web3_setVerbosity(int _v, string const& _session)
{
ADMIN;
g_logVerbosity = _v;
return true;
}
bool WebThreeStubServerBase::admin_net_start(std::string const& _session)
{
ADMIN;
network()->startNetwork();
return true;
}
bool WebThreeStubServerBase::admin_net_stop(std::string const& _session)
{
ADMIN;
network()->stopNetwork();
return true;
}
bool WebThreeStubServerBase::admin_net_connect(std::string const& _node, std::string const& _session)
{
ADMIN;
p2p::NodeId id;
bi::tcp::endpoint ep;
if (_node.substr(0, 8) == "enode://" && _node.find('@') == 136)
{
id = p2p::NodeId(_node.substr(8, 128));
ep = p2p::Network::resolveHost(_node.substr(137));
}
else
ep = p2p::Network::resolveHost(_node);
network()->requirePeer(id, ep);
return true;
}
Json::Value WebThreeStubServerBase::admin_net_peers(std::string const& _session)
{
ADMIN;
Json::Value ret;
for (p2p::PeerSessionInfo const& i: network()->peers())
ret.append(toJson(i));
return ret;
}
bool WebThreeStubServerBase::admin_eth_setMining(bool _on, std::string const& _session)
{
ADMIN;
if (_on)
client()->startMining();
else
client()->stopMining();
return true;
}
Json::Value WebThreeStubServerBase::eth_compileSolidity(string const& _source) Json::Value WebThreeStubServerBase::eth_compileSolidity(string const& _source)
{ {
// TOOD throw here jsonrpc errors // TOOD throw here jsonrpc errors
@ -893,8 +626,6 @@ bool WebThreeStubServerBase::eth_uninstallFilter(string const& _filterId)
{ {
BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS)); BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS));
} }
} }
Json::Value WebThreeStubServerBase::eth_getFilterChanges(string const& _filterId) Json::Value WebThreeStubServerBase::eth_getFilterChanges(string const& _filterId)

53
libweb3jsonrpc/WebThreeStubServerBase.h

@ -26,6 +26,7 @@
#include <memory> #include <memory>
#include <iostream> #include <iostream>
#include <jsonrpccpp/server.h> #include <jsonrpccpp/server.h>
#include <jsonrpccpp/common/exception.h>
#include <libdevcrypto/Common.h> #include <libdevcrypto/Common.h>
#pragma GCC diagnostic push #pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter" #pragma GCC diagnostic ignored "-Wunused-parameter"
@ -58,6 +59,26 @@ public:
virtual void put(std::string const& _name, std::string const& _key, std::string const& _value) = 0; virtual void put(std::string const& _name, std::string const& _key, std::string const& _value) = 0;
}; };
enum class Priviledge
{
Admin
};
}
namespace std
{
template<> struct hash<dev::Priviledge>
{
size_t operator()(dev::Priviledge _value) const { return (size_t)_value; }
};
}
namespace dev
{
/** /**
* @brief JSON-RPC api implementation * @brief JSON-RPC api implementation
* @todo split these up according to subprotocol (eth, shh, db, p2p, web3) and make it /very/ clear about how to add other subprotocols. * @todo split these up according to subprotocol (eth, shh, db, p2p, web3) and make it /very/ clear about how to add other subprotocols.
@ -122,6 +143,7 @@ public:
virtual std::string eth_signTransaction(Json::Value const& _transaction); virtual std::string eth_signTransaction(Json::Value const& _transaction);
virtual Json::Value eth_inspectTransaction(std::string const& _rlp); virtual Json::Value eth_inspectTransaction(std::string const& _rlp);
virtual bool eth_injectTransaction(std::string const& _rlp); virtual bool eth_injectTransaction(std::string const& _rlp);
virtual bool eth_notePassword(std::string const&) { return false; }
virtual bool db_put(std::string const& _name, std::string const& _key, std::string const& _value); virtual bool db_put(std::string const& _name, std::string const& _key, std::string const& _value);
virtual std::string db_get(std::string const& _name, std::string const& _key); virtual std::string db_get(std::string const& _name, std::string const& _key);
@ -135,11 +157,40 @@ public:
virtual bool shh_uninstallFilter(std::string const& _filterId); virtual bool shh_uninstallFilter(std::string const& _filterId);
virtual Json::Value shh_getFilterChanges(std::string const& _filterId); virtual Json::Value shh_getFilterChanges(std::string const& _filterId);
virtual Json::Value shh_getMessages(std::string const& _filterId); virtual Json::Value shh_getMessages(std::string const& _filterId);
virtual bool admin_web3_setVerbosity(int _v, std::string const& _session);
virtual bool admin_net_start(std::string const& _session);
virtual bool admin_net_stop(std::string const& _session);
virtual bool admin_net_connect(std::string const& _node, std::string const& _session);
virtual Json::Value admin_net_peers(std::string const& _session);
virtual bool admin_eth_setMining(bool _on, std::string const& _session);
virtual Json::Value admin_eth_blockQueueStatus(std::string const& _session) { (void)_session; return Json::Value(); }
virtual bool admin_eth_setAskPrice(std::string const& _wei, std::string const& _session) { (void)_wei; (void)_session; return false; }
virtual bool admin_eth_setBidPrice(std::string const& _wei, std::string const& _session) { (void)_wei; (void)_session; return false; }
virtual Json::Value admin_eth_findBlock(std::string const& _blockHash, std::string const& _session) { (void)_blockHash; (void)_session; return Json::Value(); }
virtual std::string admin_eth_blockQueueFirstUnknown(std::string const& _session) { (void)_session; return ""; }
virtual bool admin_eth_blockQueueRetryUnknown(std::string const& _session) { (void)_session; return false; }
virtual Json::Value admin_eth_allAccounts(std::string const& _session) { (void)_session; return Json::Value(); }
virtual Json::Value admin_eth_newAccount(const Json::Value& _info, std::string const& _session) { (void)_info; (void)_session; return Json::Value(); }
virtual bool admin_eth_setMiningBenefactor(std::string const& _uuidOrAddress, std::string const& _session) { (void)_uuidOrAddress; (void)_session; return false; }
virtual Json::Value admin_eth_inspect(std::string const& _address, std::string const& _session) { (void)_address; (void)_session; return Json::Value(); }
virtual Json::Value admin_eth_reprocess(std::string const& _blockNumberOrHash, std::string const& _session) { (void)_blockNumberOrHash; (void)_session; return Json::Value(); }
virtual Json::Value admin_eth_vmTrace(std::string const& _blockNumberOrHash, int _txIndex, std::string const& _session) { (void)_blockNumberOrHash; (void)_txIndex; (void)_session; return Json::Value(); }
virtual Json::Value admin_eth_getReceiptByHashAndIndex(std::string const& _blockNumberOrHash, int _txIndex, std::string const& _session) { (void)_blockNumberOrHash; (void)_txIndex; (void)_session; return Json::Value(); }
// TODO REMOVE
virtual bool admin_eth_setReferencePrice(std::string const& _wei, std::string const& _session) { (void)_wei; (void)_session; return false; }
virtual bool admin_eth_setPriority(int _percent, std::string const& _session) { (void)_percent; (void)_session; return false; }
virtual bool admin_eth_setSigningKey(std::string const& _uuidOrAddress, std::string const& _session) { (void)_uuidOrAddress; (void)_session; return false; }
void setIdentities(std::vector<dev::KeyPair> const& _ids); void setIdentities(std::vector<dev::KeyPair> const& _ids);
std::map<dev::Public, dev::Secret> const& ids() const { return m_shhIds; } std::map<dev::Public, dev::Secret> const& ids() const { return m_shhIds; }
protected: protected:
void requires(std::string const& _session, Priviledge _l) const { if (!hasPriviledgeLevel(_session, _l)) throw jsonrpc::JsonRpcException("Invalid priviledges"); }
virtual bool hasPriviledgeLevel(std::string const& _session, Priviledge _l) const { (void)_session; (void)_l; return false; }
virtual dev::eth::Interface* client() = 0; virtual dev::eth::Interface* client() = 0;
virtual std::shared_ptr<dev::shh::Interface> face() = 0; virtual std::shared_ptr<dev::shh::Interface> face() = 0;
virtual dev::WebThreeNetworkFace* network() = 0; virtual dev::WebThreeNetworkFace* network() = 0;

138
libweb3jsonrpc/abstractwebthreestubserver.h

@ -64,6 +64,7 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServer<AbstractWebThr
this->bindAndAddMethod(jsonrpc::Procedure("eth_signTransaction", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_OBJECT, NULL), &AbstractWebThreeStubServer::eth_signTransactionI); this->bindAndAddMethod(jsonrpc::Procedure("eth_signTransaction", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_OBJECT, NULL), &AbstractWebThreeStubServer::eth_signTransactionI);
this->bindAndAddMethod(jsonrpc::Procedure("eth_inspectTransaction", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_inspectTransactionI); this->bindAndAddMethod(jsonrpc::Procedure("eth_inspectTransaction", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_inspectTransactionI);
this->bindAndAddMethod(jsonrpc::Procedure("eth_injectTransaction", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_injectTransactionI); this->bindAndAddMethod(jsonrpc::Procedure("eth_injectTransaction", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_injectTransactionI);
this->bindAndAddMethod(jsonrpc::Procedure("eth_notePassword", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_notePasswordI);
this->bindAndAddMethod(jsonrpc::Procedure("db_put", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING,"param3",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::db_putI); this->bindAndAddMethod(jsonrpc::Procedure("db_put", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING,"param3",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::db_putI);
this->bindAndAddMethod(jsonrpc::Procedure("db_get", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::db_getI); this->bindAndAddMethod(jsonrpc::Procedure("db_get", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::db_getI);
this->bindAndAddMethod(jsonrpc::Procedure("shh_post", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_OBJECT, NULL), &AbstractWebThreeStubServer::shh_postI); this->bindAndAddMethod(jsonrpc::Procedure("shh_post", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_OBJECT, NULL), &AbstractWebThreeStubServer::shh_postI);
@ -75,6 +76,28 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServer<AbstractWebThr
this->bindAndAddMethod(jsonrpc::Procedure("shh_uninstallFilter", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::shh_uninstallFilterI); this->bindAndAddMethod(jsonrpc::Procedure("shh_uninstallFilter", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::shh_uninstallFilterI);
this->bindAndAddMethod(jsonrpc::Procedure("shh_getFilterChanges", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::shh_getFilterChangesI); this->bindAndAddMethod(jsonrpc::Procedure("shh_getFilterChanges", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::shh_getFilterChangesI);
this->bindAndAddMethod(jsonrpc::Procedure("shh_getMessages", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::shh_getMessagesI); this->bindAndAddMethod(jsonrpc::Procedure("shh_getMessages", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::shh_getMessagesI);
this->bindAndAddMethod(jsonrpc::Procedure("admin_web3_setVerbosity", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_INTEGER,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_web3_setVerbosityI);
this->bindAndAddMethod(jsonrpc::Procedure("admin_net_start", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_net_startI);
this->bindAndAddMethod(jsonrpc::Procedure("admin_net_stop", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_net_stopI);
this->bindAndAddMethod(jsonrpc::Procedure("admin_net_connect", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_net_connectI);
this->bindAndAddMethod(jsonrpc::Procedure("admin_net_peers", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_net_peersI);
this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_blockQueueStatus", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_eth_blockQueueStatusI);
this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_setAskPrice", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_eth_setAskPriceI);
this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_setBidPrice", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_eth_setBidPriceI);
this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_setReferencePrice", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_eth_setReferencePriceI);
this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_setPriority", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_INTEGER,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_eth_setPriorityI);
this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_setMining", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_BOOLEAN,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_eth_setMiningI);
this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_findBlock", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_eth_findBlockI);
this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_blockQueueFirstUnknown", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_eth_blockQueueFirstUnknownI);
this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_blockQueueRetryUnknown", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_eth_blockQueueRetryUnknownI);
this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_allAccounts", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_eth_allAccountsI);
this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_newAccount", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_OBJECT,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_eth_newAccountI);
this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_setSigningKey", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_eth_setSigningKeyI);
this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_setMiningBenefactor", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_eth_setMiningBenefactorI);
this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_inspect", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_eth_inspectI);
this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_reprocess", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_eth_reprocessI);
this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_vmTrace", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_INTEGER,"param3",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_eth_vmTraceI);
this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_getReceiptByHashAndIndex", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_INTEGER,"param3",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_eth_getReceiptByHashAndIndexI);
} }
inline virtual void web3_sha3I(const Json::Value &request, Json::Value &response) inline virtual void web3_sha3I(const Json::Value &request, Json::Value &response)
@ -301,6 +324,10 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServer<AbstractWebThr
{ {
response = this->eth_injectTransaction(request[0u].asString()); response = this->eth_injectTransaction(request[0u].asString());
} }
inline virtual void eth_notePasswordI(const Json::Value &request, Json::Value &response)
{
response = this->eth_notePassword(request[0u].asString());
}
inline virtual void db_putI(const Json::Value &request, Json::Value &response) inline virtual void db_putI(const Json::Value &request, Json::Value &response)
{ {
response = this->db_put(request[0u].asString(), request[1u].asString(), request[2u].asString()); response = this->db_put(request[0u].asString(), request[1u].asString(), request[2u].asString());
@ -346,6 +373,94 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServer<AbstractWebThr
{ {
response = this->shh_getMessages(request[0u].asString()); response = this->shh_getMessages(request[0u].asString());
} }
inline virtual void admin_web3_setVerbosityI(const Json::Value &request, Json::Value &response)
{
response = this->admin_web3_setVerbosity(request[0u].asInt(), request[1u].asString());
}
inline virtual void admin_net_startI(const Json::Value &request, Json::Value &response)
{
response = this->admin_net_start(request[0u].asString());
}
inline virtual void admin_net_stopI(const Json::Value &request, Json::Value &response)
{
response = this->admin_net_stop(request[0u].asString());
}
inline virtual void admin_net_connectI(const Json::Value &request, Json::Value &response)
{
response = this->admin_net_connect(request[0u].asString(), request[1u].asString());
}
inline virtual void admin_net_peersI(const Json::Value &request, Json::Value &response)
{
response = this->admin_net_peers(request[0u].asString());
}
inline virtual void admin_eth_blockQueueStatusI(const Json::Value &request, Json::Value &response)
{
response = this->admin_eth_blockQueueStatus(request[0u].asString());
}
inline virtual void admin_eth_setAskPriceI(const Json::Value &request, Json::Value &response)
{
response = this->admin_eth_setAskPrice(request[0u].asString(), request[1u].asString());
}
inline virtual void admin_eth_setBidPriceI(const Json::Value &request, Json::Value &response)
{
response = this->admin_eth_setBidPrice(request[0u].asString(), request[1u].asString());
}
inline virtual void admin_eth_setReferencePriceI(const Json::Value &request, Json::Value &response)
{
response = this->admin_eth_setReferencePrice(request[0u].asString(), request[1u].asString());
}
inline virtual void admin_eth_setPriorityI(const Json::Value &request, Json::Value &response)
{
response = this->admin_eth_setPriority(request[0u].asInt(), request[1u].asString());
}
inline virtual void admin_eth_setMiningI(const Json::Value &request, Json::Value &response)
{
response = this->admin_eth_setMining(request[0u].asBool(), request[1u].asString());
}
inline virtual void admin_eth_findBlockI(const Json::Value &request, Json::Value &response)
{
response = this->admin_eth_findBlock(request[0u].asString(), request[1u].asString());
}
inline virtual void admin_eth_blockQueueFirstUnknownI(const Json::Value &request, Json::Value &response)
{
response = this->admin_eth_blockQueueFirstUnknown(request[0u].asString());
}
inline virtual void admin_eth_blockQueueRetryUnknownI(const Json::Value &request, Json::Value &response)
{
response = this->admin_eth_blockQueueRetryUnknown(request[0u].asString());
}
inline virtual void admin_eth_allAccountsI(const Json::Value &request, Json::Value &response)
{
response = this->admin_eth_allAccounts(request[0u].asString());
}
inline virtual void admin_eth_newAccountI(const Json::Value &request, Json::Value &response)
{
response = this->admin_eth_newAccount(request[0u], request[1u].asString());
}
inline virtual void admin_eth_setSigningKeyI(const Json::Value &request, Json::Value &response)
{
response = this->admin_eth_setSigningKey(request[0u].asString(), request[1u].asString());
}
inline virtual void admin_eth_setMiningBenefactorI(const Json::Value &request, Json::Value &response)
{
response = this->admin_eth_setMiningBenefactor(request[0u].asString(), request[1u].asString());
}
inline virtual void admin_eth_inspectI(const Json::Value &request, Json::Value &response)
{
response = this->admin_eth_inspect(request[0u].asString(), request[1u].asString());
}
inline virtual void admin_eth_reprocessI(const Json::Value &request, Json::Value &response)
{
response = this->admin_eth_reprocess(request[0u].asString(), request[1u].asString());
}
inline virtual void admin_eth_vmTraceI(const Json::Value &request, Json::Value &response)
{
response = this->admin_eth_vmTrace(request[0u].asString(), request[1u].asInt(), request[2u].asString());
}
inline virtual void admin_eth_getReceiptByHashAndIndexI(const Json::Value &request, Json::Value &response)
{
response = this->admin_eth_getReceiptByHashAndIndex(request[0u].asString(), request[1u].asInt(), request[2u].asString());
}
virtual std::string web3_sha3(const std::string& param1) = 0; virtual std::string web3_sha3(const std::string& param1) = 0;
virtual std::string web3_clientVersion() = 0; virtual std::string web3_clientVersion() = 0;
virtual std::string net_version() = 0; virtual std::string net_version() = 0;
@ -398,6 +513,7 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServer<AbstractWebThr
virtual std::string eth_signTransaction(const Json::Value& param1) = 0; virtual std::string eth_signTransaction(const Json::Value& param1) = 0;
virtual Json::Value eth_inspectTransaction(const std::string& param1) = 0; virtual Json::Value eth_inspectTransaction(const std::string& param1) = 0;
virtual bool eth_injectTransaction(const std::string& param1) = 0; virtual bool eth_injectTransaction(const std::string& param1) = 0;
virtual bool eth_notePassword(const std::string& param1) = 0;
virtual bool db_put(const std::string& param1, const std::string& param2, const std::string& param3) = 0; virtual bool db_put(const std::string& param1, const std::string& param2, const std::string& param3) = 0;
virtual std::string db_get(const std::string& param1, const std::string& param2) = 0; virtual std::string db_get(const std::string& param1, const std::string& param2) = 0;
virtual bool shh_post(const Json::Value& param1) = 0; virtual bool shh_post(const Json::Value& param1) = 0;
@ -409,6 +525,28 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServer<AbstractWebThr
virtual bool shh_uninstallFilter(const std::string& param1) = 0; virtual bool shh_uninstallFilter(const std::string& param1) = 0;
virtual Json::Value shh_getFilterChanges(const std::string& param1) = 0; virtual Json::Value shh_getFilterChanges(const std::string& param1) = 0;
virtual Json::Value shh_getMessages(const std::string& param1) = 0; virtual Json::Value shh_getMessages(const std::string& param1) = 0;
virtual bool admin_web3_setVerbosity(int param1, const std::string& param2) = 0;
virtual bool admin_net_start(const std::string& param1) = 0;
virtual bool admin_net_stop(const std::string& param1) = 0;
virtual bool admin_net_connect(const std::string& param1, const std::string& param2) = 0;
virtual Json::Value admin_net_peers(const std::string& param1) = 0;
virtual Json::Value admin_eth_blockQueueStatus(const std::string& param1) = 0;
virtual bool admin_eth_setAskPrice(const std::string& param1, const std::string& param2) = 0;
virtual bool admin_eth_setBidPrice(const std::string& param1, const std::string& param2) = 0;
virtual bool admin_eth_setReferencePrice(const std::string& param1, const std::string& param2) = 0;
virtual bool admin_eth_setPriority(int param1, const std::string& param2) = 0;
virtual bool admin_eth_setMining(bool param1, const std::string& param2) = 0;
virtual Json::Value admin_eth_findBlock(const std::string& param1, const std::string& param2) = 0;
virtual std::string admin_eth_blockQueueFirstUnknown(const std::string& param1) = 0;
virtual bool admin_eth_blockQueueRetryUnknown(const std::string& param1) = 0;
virtual Json::Value admin_eth_allAccounts(const std::string& param1) = 0;
virtual Json::Value admin_eth_newAccount(const Json::Value& param1, const std::string& param2) = 0;
virtual bool admin_eth_setSigningKey(const std::string& param1, const std::string& param2) = 0;
virtual bool admin_eth_setMiningBenefactor(const std::string& param1, const std::string& param2) = 0;
virtual Json::Value admin_eth_inspect(const std::string& param1, const std::string& param2) = 0;
virtual Json::Value admin_eth_reprocess(const std::string& param1, const std::string& param2) = 0;
virtual Json::Value admin_eth_vmTrace(const std::string& param1, int param2, const std::string& param3) = 0;
virtual Json::Value admin_eth_getReceiptByHashAndIndex(const std::string& param1, int param2, const std::string& param3) = 0;
}; };
#endif //JSONRPC_CPP_STUB_ABSTRACTWEBTHREESTUBSERVER_H_ #endif //JSONRPC_CPP_STUB_ABSTRACTWEBTHREESTUBSERVER_H_

27
libweb3jsonrpc/spec.json

@ -53,6 +53,7 @@
{ "name": "eth_signTransaction", "params": [{}], "order": [], "returns": ""}, { "name": "eth_signTransaction", "params": [{}], "order": [], "returns": ""},
{ "name": "eth_inspectTransaction", "params": [""], "order": [], "returns": {}}, { "name": "eth_inspectTransaction", "params": [""], "order": [], "returns": {}},
{ "name": "eth_injectTransaction", "params": [""], "order": [], "returns": true}, { "name": "eth_injectTransaction", "params": [""], "order": [], "returns": true},
{ "name": "eth_notePassword", "params": [""], "order": [], "returns": true},
{ "name": "db_put", "params": ["", "", ""], "order": [], "returns": true}, { "name": "db_put", "params": ["", "", ""], "order": [], "returns": true},
{ "name": "db_get", "params": ["", ""], "order": [], "returns": ""}, { "name": "db_get", "params": ["", ""], "order": [], "returns": ""},
@ -65,6 +66,30 @@
{ "name": "shh_newFilter", "params": [{}], "order": [], "returns": ""}, { "name": "shh_newFilter", "params": [{}], "order": [], "returns": ""},
{ "name": "shh_uninstallFilter", "params": [""], "order": [], "returns": true}, { "name": "shh_uninstallFilter", "params": [""], "order": [], "returns": true},
{ "name": "shh_getFilterChanges", "params": [""], "order": [], "returns": []}, { "name": "shh_getFilterChanges", "params": [""], "order": [], "returns": []},
{ "name": "shh_getMessages", "params": [""], "order": [], "returns": []} { "name": "shh_getMessages", "params": [""], "order": [], "returns": []},
{ "name": "admin_web3_setVerbosity", "params": [0, ""], "returns": true },
{ "name": "admin_net_start", "params": [""], "returns": true },
{ "name": "admin_net_stop", "params": [""], "returns": true },
{ "name": "admin_net_connect", "params": ["", ""], "returns": true },
{ "name": "admin_net_peers", "params": [""], "returns": [] },
{ "name": "admin_eth_blockQueueStatus", "params": [""], "returns": {}},
{ "name": "admin_eth_setAskPrice", "params": ["", ""], "returns": true },
{ "name": "admin_eth_setBidPrice", "params": ["", ""], "returns": true },
{ "name": "admin_eth_setReferencePrice", "params": ["", ""], "returns": true },
{ "name": "admin_eth_setPriority", "params": [0, ""], "returns": true },
{ "name": "admin_eth_setMining", "params": [true, ""], "returns": true },
{ "name": "admin_eth_findBlock", "params": ["", ""], "returns": {} },
{ "name": "admin_eth_blockQueueFirstUnknown", "params": [""], "returns": "" },
{ "name": "admin_eth_blockQueueRetryUnknown", "params": [""], "returns": true },
{ "name": "admin_eth_allAccounts", "params": [""], "returns": [] },
{ "name": "admin_eth_newAccount", "params": [{}, ""], "returns": {} },
{ "name": "admin_eth_setSigningKey", "params": ["", ""], "returns": true },
{ "name": "admin_eth_setMiningBenefactor", "params": ["", ""], "returns": true },
{ "name": "admin_eth_inspect", "params": ["", ""], "returns": {} },
{ "name": "admin_eth_reprocess", "params": ["", ""], "returns": {} },
{ "name": "admin_eth_vmTrace", "params": ["", 0, ""], "returns": {} },
{ "name": "admin_eth_getReceiptByHashAndIndex", "params": ["", 0, ""], "returns": {} }
] ]

11
libwebthree/WebThree.cpp

@ -30,6 +30,7 @@
#include <libethereum/Defaults.h> #include <libethereum/Defaults.h>
#include <libethereum/EthereumHost.h> #include <libethereum/EthereumHost.h>
#include <libwhisper/WhisperHost.h> #include <libwhisper/WhisperHost.h>
#include "BuildInfo.h"
using namespace std; using namespace std;
using namespace dev; using namespace dev;
using namespace dev::p2p; using namespace dev::p2p;
@ -72,6 +73,16 @@ WebThreeDirect::~WebThreeDirect()
m_ethereum.reset(); m_ethereum.reset();
} }
std::string WebThreeDirect::composeClientVersion(std::string const& _client, std::string const& _clientName)
{
#if ETH_EVMJIT
char const* jit = "-JIT";
#else
char const* jit = "";
#endif
return _client + "v" + dev::Version + "-" + string(DEV_QUOTED(ETH_COMMIT_HASH)).substr(0, 8) + (ETH_CLEAN_REPO ? "" : "*") + "/" + _clientName + "/" DEV_QUOTED(ETH_BUILD_TYPE) "-" DEV_QUOTED(ETH_BUILD_PLATFORM) + jit;
}
p2p::NetworkPreferences const& WebThreeDirect::networkPreferences() const p2p::NetworkPreferences const& WebThreeDirect::networkPreferences() const
{ {
return m_net.networkPreferences(); return m_net.networkPreferences();

2
libwebthree/WebThree.h

@ -129,6 +129,8 @@ public:
// Misc stuff: // Misc stuff:
static std::string composeClientVersion(std::string const& _client, std::string const& _name);
std::string const& clientVersion() const { return m_clientVersion; } std::string const& clientVersion() const { return m_clientVersion; }
void setClientVersion(std::string const& _name) { m_clientVersion = _name; } void setClientVersion(std::string const& _name) { m_clientVersion = _name; }

259
mix/ClientModel.cpp

@ -30,6 +30,7 @@
#include <jsonrpccpp/server.h> #include <jsonrpccpp/server.h>
#include <libethcore/CommonJS.h> #include <libethcore/CommonJS.h>
#include <libethereum/Transaction.h> #include <libethereum/Transaction.h>
#include <libdevcore/FixedHash.h>
#include "DebuggingStateWrapper.h" #include "DebuggingStateWrapper.h"
#include "Exceptions.h" #include "Exceptions.h"
#include "QContractDefinition.h" #include "QContractDefinition.h"
@ -82,7 +83,6 @@ ClientModel::ClientModel():
qRegisterMetaType<QCallData*>("QCallData"); qRegisterMetaType<QCallData*>("QCallData");
qRegisterMetaType<RecordLogEntry*>("RecordLogEntry*"); qRegisterMetaType<RecordLogEntry*>("RecordLogEntry*");
connect(this, &ClientModel::runComplete, this, &ClientModel::showDebugger, Qt::QueuedConnection);
m_client.reset(new MixClient(QStandardPaths::writableLocation(QStandardPaths::TempLocation).toStdString())); m_client.reset(new MixClient(QStandardPaths::writableLocation(QStandardPaths::TempLocation).toStdString()));
m_ethAccounts = make_shared<FixedAccountHolder>([=](){return m_client.get();}, std::vector<KeyPair>()); m_ethAccounts = make_shared<FixedAccountHolder>([=](){return m_client.get();}, std::vector<KeyPair>());
@ -111,7 +111,7 @@ QString ClientModel::apiCall(QString const& _message)
void ClientModel::mine() void ClientModel::mine()
{ {
if (m_running || m_mining) if (m_mining)
BOOST_THROW_EXCEPTION(ExecutionStateException()); BOOST_THROW_EXCEPTION(ExecutionStateException());
m_mining = true; m_mining = true;
emit miningStarted(); emit miningStarted();
@ -206,15 +206,17 @@ QVariantList ClientModel::gasCosts() const
return res; return res;
} }
void ClientModel::setupState(QVariantMap _state) void ClientModel::setupScenario(QVariantMap _scenario)
{ {
QVariantList stateAccounts = _state.value("accounts").toList(); onStateReset();
QVariantList stateContracts = _state.value("contracts").toList(); WriteGuard(x_queueTransactions);
QVariantList transactions = _state.value("transactions").toList(); m_running = true;
unordered_map<Address, Account> accounts; QVariantList blocks = _scenario.value("blocks").toList();
std::vector<KeyPair> userAccounts; QVariantList stateAccounts = _scenario.value("accounts").toList();
m_accounts.clear();
m_accountsSecret.clear();
for (auto const& b: stateAccounts) for (auto const& b: stateAccounts)
{ {
QVariantMap account = b.toMap(); QVariantMap account = b.toMap();
@ -222,7 +224,7 @@ void ClientModel::setupState(QVariantMap _state)
if (account.contains("secret")) if (account.contains("secret"))
{ {
KeyPair key(Secret(account.value("secret").toString().toStdString())); KeyPair key(Secret(account.value("secret").toString().toStdString()));
userAccounts.push_back(key); m_accountsSecret.push_back(key);
address = key.address(); address = key.address();
} }
else if (account.contains("address")) else if (account.contains("address"))
@ -230,29 +232,70 @@ void ClientModel::setupState(QVariantMap _state)
if (!address) if (!address)
continue; continue;
accounts[address] = Account(qvariant_cast<QEther*>(account.value("balance"))->toU256Wei(), Account::NormalCreation); m_accounts[address] = Account(qvariant_cast<QEther*>(account.value("balance"))->toU256Wei(), Account::NormalCreation);
} }
for (auto const& c: stateContracts) m_ethAccounts->setAccounts(m_accountsSecret);
bool trToExecute = false;
for (auto const& b: blocks)
{ {
QVariantMap contract = c.toMap(); QVariantList transactions = b.toMap().value("transactions").toList();
Address address = Address(fromHex(contract.value("address").toString().toStdString())); m_queueTransactions.push_back(transactions);
Account account(qvariant_cast<QEther*>(contract.value("balance"))->toU256Wei(), Account::ContractConception); trToExecute = transactions.size() > 0;
bytes code = fromHex(contract.value("code").toString().toStdString());
account.setCode(code);
QVariantMap storageMap = contract.value("storage").toMap();
for(auto s = storageMap.cbegin(); s != storageMap.cend(); ++s)
account.setStorage(fromBigEndian<u256>(fromHex(s.key().toStdString())), fromBigEndian<u256>(fromHex(s.value().toString().toStdString())));
accounts[address] = account;
} }
m_client->resetState(m_accounts, Secret(_scenario.value("miner").toMap().value("secret").toString().toStdString()));
if (m_queueTransactions.count() > 0 && trToExecute)
{
setupExecutionChain();
processNextTransactions();
}
else
m_running = false;
}
void ClientModel::setupExecutionChain()
{
connect(this, &ClientModel::newBlock, this, &ClientModel::processNextTransactions, Qt::QueuedConnection);
connect(this, &ClientModel::runFailed, this, &ClientModel::stopExecution, Qt::QueuedConnection);
connect(this, &ClientModel::runStateChanged, this, &ClientModel::finalizeBlock, Qt::QueuedConnection);
}
void ClientModel::stopExecution()
{
disconnect(this, &ClientModel::newBlock, this, &ClientModel::processNextTransactions);
disconnect(this, &ClientModel::runStateChanged, this, &ClientModel::finalizeBlock);
disconnect(this, &ClientModel::runFailed, this, &ClientModel::stopExecution);
m_running = false;
}
void ClientModel::finalizeBlock()
{
m_queueTransactions.pop_front();// pop last execution group. The last block is never mined (pending block)
if (m_queueTransactions.size() > 0)
mine();
else
{
stopExecution();
emit runComplete();
}
}
void ClientModel::processNextTransactions()
{
WriteGuard(x_queueTransactions);
vector<TransactionSettings> transactionSequence; vector<TransactionSettings> transactionSequence;
for (auto const& t: transactions) for (auto const& t: m_queueTransactions.front())
{ {
QVariantMap transaction = t.toMap(); QVariantMap transaction = t.toMap();
QString contractId = transaction.value("contractId").toString(); QString contractId = transaction.value("contractId").toString();
QString functionId = transaction.value("functionId").toString(); QString functionId = transaction.value("functionId").toString();
u256 gas = boost::get<u256>(qvariant_cast<QBigInt*>(transaction.value("gas"))->internalValue());
bool gasAuto = transaction.value("gasAuto").toBool(); bool gasAuto = transaction.value("gasAuto").toBool();
u256 gas = 0;
if (transaction.value("gas").data())
gas = boost::get<u256>(qvariant_cast<QBigInt*>(transaction.value("gas"))->internalValue());
else
gasAuto = true;
u256 value = (qvariant_cast<QEther*>(transaction.value("value")))->toU256Wei(); u256 value = (qvariant_cast<QEther*>(transaction.value("value")))->toU256Wei();
u256 gasPrice = (qvariant_cast<QEther*>(transaction.value("gasPrice")))->toU256Wei(); u256 gasPrice = (qvariant_cast<QEther*>(transaction.value("gasPrice")))->toU256Wei();
QString sender = transaction.value("sender").toString(); QString sender = transaction.value("sender").toString();
@ -260,7 +303,8 @@ void ClientModel::setupState(QVariantMap _state)
bool isFunctionCall = transaction.value("isFunctionCall").toBool(); bool isFunctionCall = transaction.value("isFunctionCall").toBool();
if (contractId.isEmpty() && m_codeModel->hasContract()) //TODO: This is to support old project files, remove later if (contractId.isEmpty() && m_codeModel->hasContract()) //TODO: This is to support old project files, remove later
contractId = m_codeModel->contracts().keys()[0]; contractId = m_codeModel->contracts().keys()[0];
TransactionSettings transactionSettings(contractId, functionId, value, gas, gasAuto, gasPrice, Secret(sender.toStdString()), isContractCreation, isFunctionCall); Secret f = Secret(sender.toStdString());
TransactionSettings transactionSettings(contractId, functionId, value, gas, gasAuto, gasPrice, f, isContractCreation, isFunctionCall);
transactionSettings.parameterValues = transaction.value("parameters").toMap(); transactionSettings.parameterValues = transaction.value("parameters").toMap();
if (contractId == functionId || functionId == "Constructor") if (contractId == functionId || functionId == "Constructor")
@ -268,35 +312,27 @@ void ClientModel::setupState(QVariantMap _state)
transactionSequence.push_back(transactionSettings); transactionSequence.push_back(transactionSettings);
} }
m_ethAccounts->setAccounts(userAccounts); executeSequence(transactionSequence);
executeSequence(transactionSequence, accounts, Secret(_state.value("miner").toMap().value("secret").toString().toStdString()));
} }
void ClientModel::executeSequence(vector<TransactionSettings> const& _sequence, std::unordered_map<Address, Account> const& _accounts, Secret const& _miner) void ClientModel::executeSequence(vector<TransactionSettings> const& _sequence)
{ {
if (m_running) if (m_running)
{ {
qWarning() << "Waiting for current execution to complete"; qWarning() << "Waiting for current execution to complete";
m_runFuture.waitForFinished(); m_runFuture.waitForFinished();
} }
m_running = true;
emit runStarted(); emit runStarted();
emit runStateChanged();
m_client->resetState(_accounts, _miner);
//run sequence //run sequence
m_runFuture = QtConcurrent::run([=]() m_runFuture = QtConcurrent::run([=]()
{ {
try try
{ {
vector<Address> deployedContracts;
onStateReset();
m_gasCosts.clear(); m_gasCosts.clear();
for (TransactionSettings const& transaction: _sequence) for (TransactionSettings const& transaction: _sequence)
{ {
std::pair<QString, int> ctrInstance = resolvePair(transaction.contractId); std::pair<QString, int> ctrInstance = resolvePair(transaction.contractId);
QString address = resolveToken(ctrInstance, deployedContracts); QString address = resolveToken(ctrInstance);
if (!transaction.isFunctionCall) if (!transaction.isFunctionCall)
{ {
callAddress(Address(address.toStdString()), bytes(), transaction); callAddress(Address(address.toStdString()), bytes(), transaction);
@ -319,12 +355,7 @@ void ClientModel::executeSequence(vector<TransactionSettings> const& _sequence,
break; break;
} }
if (!f) if (!f)
{
emit runFailed("Function '" + transaction.functionId + tr("' not found. Please check transactions or the contract code.")); emit runFailed("Function '" + transaction.functionId + tr("' not found. Please check transactions or the contract code."));
m_running = false;
emit runStateChanged();
return;
}
if (!transaction.functionId.isEmpty()) if (!transaction.functionId.isEmpty())
encoder.encode(f); encoder.encode(f);
for (QVariableDeclaration const* p: f->parametersList()) for (QVariableDeclaration const* p: f->parametersList())
@ -334,7 +365,7 @@ void ClientModel::executeSequence(vector<TransactionSettings> const& _sequence,
if (type->type().type == SolidityType::Type::Address && value.toString().startsWith("<")) if (type->type().type == SolidityType::Type::Address && value.toString().startsWith("<"))
{ {
std::pair<QString, int> ctrParamInstance = resolvePair(value.toString()); std::pair<QString, int> ctrParamInstance = resolvePair(value.toString());
value = QVariant(resolveToken(ctrParamInstance, deployedContracts)); value = QVariant(resolveToken(ctrParamInstance));
} }
encoder.encode(value, type->type()); encoder.encode(value, type->type());
} }
@ -344,8 +375,7 @@ void ClientModel::executeSequence(vector<TransactionSettings> const& _sequence,
bytes param = encoder.encodedData(); bytes param = encoder.encodedData();
contractCode.insert(contractCode.end(), param.begin(), param.end()); contractCode.insert(contractCode.end(), param.begin(), param.end());
Address newAddress = deployContract(contractCode, transaction); Address newAddress = deployContract(contractCode, transaction);
deployedContracts.push_back(newAddress); std::pair<QString, int> contractToken = retrieveToken(transaction.contractId);
std::pair<QString, int> contractToken = retrieveToken(transaction.contractId, deployedContracts);
m_contractAddresses[contractToken] = newAddress; m_contractAddresses[contractToken] = newAddress;
m_contractNames[newAddress] = contractToken.first; m_contractNames[newAddress] = contractToken.first;
contractAddressesChanged(); contractAddressesChanged();
@ -355,18 +385,12 @@ void ClientModel::executeSequence(vector<TransactionSettings> const& _sequence,
{ {
auto contractAddressIter = m_contractAddresses.find(ctrInstance); auto contractAddressIter = m_contractAddresses.find(ctrInstance);
if (contractAddressIter == m_contractAddresses.end()) if (contractAddressIter == m_contractAddresses.end())
{
emit runFailed("Contract '" + transaction.contractId + tr(" not deployed.") + "' " + tr(" Cannot call ") + transaction.functionId); emit runFailed("Contract '" + transaction.contractId + tr(" not deployed.") + "' " + tr(" Cannot call ") + transaction.functionId);
m_running = false;
emit runStateChanged();
return;
}
callAddress(contractAddressIter->second, encoder.encodedData(), transaction); callAddress(contractAddressIter->second, encoder.encodedData(), transaction);
} }
m_gasCosts.append(m_client->lastExecution().gasUsed); m_gasCosts.append(m_client->lastExecution().gasUsed);
onNewTransaction(); onNewTransaction();
} }
m_running = false;
emit runComplete(); emit runComplete();
} }
catch(boost::exception const&) catch(boost::exception const&)
@ -379,37 +403,54 @@ void ClientModel::executeSequence(vector<TransactionSettings> const& _sequence,
cerr << boost::current_exception_diagnostic_information(); cerr << boost::current_exception_diagnostic_information();
emit runFailed(e.what()); emit runFailed(e.what());
} }
m_running = false;
emit runStateChanged(); emit runStateChanged();
}); });
} }
void ClientModel::executeTr(QVariantMap _tr)
{
WriteGuard(x_queueTransactions);
QVariantList trs;
trs.push_back(_tr);
m_queueTransactions.push_back(trs);
if (!m_running)
{
m_running = true;
setupExecutionChain();
processNextTransactions();
}
}
std::pair<QString, int> ClientModel::resolvePair(QString const& _contractId) std::pair<QString, int> ClientModel::resolvePair(QString const& _contractId)
{ {
std::pair<QString, int> ret = std::make_pair(_contractId, 0); std::pair<QString, int> ret = std::make_pair(_contractId, 0);
if (_contractId.startsWith("<") && _contractId.endsWith(">")) if (_contractId.startsWith("<") && _contractId.endsWith(">"))
{ {
QStringList values = ret.first.remove("<").remove(">").split(" - "); QStringList values = ret.first.remove("<").remove(">").split(" - ");
ret = std::make_pair(values[0], values[1].toUInt()); ret = std::make_pair(values[0], values[1].toUInt());
} }
return ret; if (_contractId.startsWith("0x"))
ret = std::make_pair(_contractId, -2);
return ret;
} }
QString ClientModel::resolveToken(std::pair<QString, int> const& _value, vector<Address> const& _contracts) QString ClientModel::resolveToken(std::pair<QString, int> const& _value)
{ {
if (_contracts.size() > 0) if (_value.second == -2) //-2: first contains a real address
return QString::fromStdString("0x" + dev::toHex(_contracts.at(_value.second).ref())); return _value.first;
else if (m_contractAddresses.size() > 0)
return QString::fromStdString("0x" + dev::toHex(m_contractAddresses[_value].ref()));
else else
return _value.first; return _value.first;
} }
std::pair<QString, int> ClientModel::retrieveToken(QString const& _value, vector<Address> const& _contracts) std::pair<QString, int> ClientModel::retrieveToken(QString const& _value)
{ {
std::pair<QString, int> ret; std::pair<QString, int> ret;
ret.first = _value; ret.first = _value;
ret.second = _contracts.size() - 1; ret.second = m_contractAddresses.size();
return ret; return ret;
} }
void ClientModel::showDebugger() void ClientModel::showDebugger()
@ -633,7 +674,7 @@ RecordLogEntry* ClientModel::lastBlock() const
strGas << blockInfo.gasUsed; strGas << blockInfo.gasUsed;
stringstream strNumber; stringstream strNumber;
strNumber << blockInfo.number; strNumber << blockInfo.number;
RecordLogEntry* record = new RecordLogEntry(0, QString::fromStdString(strNumber.str()), tr(" - Block - "), tr("Hash: ") + QString(QString::fromStdString(dev::toHex(blockInfo.hash().ref()))), QString(), QString(), QString(), false, RecordLogEntry::RecordType::Block, QString::fromStdString(strGas.str())); RecordLogEntry* record = new RecordLogEntry(0, QString::fromStdString(strNumber.str()), tr(" - Block - "), tr("Hash: ") + QString(QString::fromStdString(dev::toHex(blockInfo.hash().ref()))), QString(), QString(), QString(), false, RecordLogEntry::RecordType::Block, QString::fromStdString(strGas.str()), QString(), tr("Block"), QVariantMap(), QVariantList());
QQmlEngine::setObjectOwnership(record, QQmlEngine::JavaScriptOwnership); QQmlEngine::setObjectOwnership(record, QQmlEngine::JavaScriptOwnership);
return record; return record;
} }
@ -644,6 +685,7 @@ void ClientModel::onStateReset()
m_contractNames.clear(); m_contractNames.clear();
m_stdContractAddresses.clear(); m_stdContractAddresses.clear();
m_stdContractNames.clear(); m_stdContractNames.clear();
m_queueTransactions.clear();
emit stateCleared(); emit stateCleared();
} }
@ -692,27 +734,110 @@ void ClientModel::onNewTransaction()
Address contractAddress = (bool)tr.address ? tr.address : tr.contractAddress; Address contractAddress = (bool)tr.address ? tr.address : tr.contractAddress;
auto contractAddressIter = m_contractNames.find(contractAddress); auto contractAddressIter = m_contractNames.find(contractAddress);
QVariantMap inputParameters;
QVariantList logs;
if (contractAddressIter != m_contractNames.end()) if (contractAddressIter != m_contractNames.end())
{ {
ContractCallDataEncoder encoder;
CompiledContract const& compilerRes = m_codeModel->contract(contractAddressIter->second); CompiledContract const& compilerRes = m_codeModel->contract(contractAddressIter->second);
const QContractDefinition* def = compilerRes.contract(); const QContractDefinition* def = compilerRes.contract();
contract = def->name(); contract = def->name();
if (creation)
function = contract;
if (abi) if (abi)
{ {
QFunctionDefinition const* funcDef = def->getFunction(functionHash); QFunctionDefinition const* funcDef = def->getFunction(functionHash);
if (funcDef) if (funcDef)
{ {
function = funcDef->name(); function = funcDef->name();
ContractCallDataEncoder encoder;
QStringList returnValues = encoder.decode(funcDef->returnParameters(), tr.result.output); QStringList returnValues = encoder.decode(funcDef->returnParameters(), tr.result.output);
returned += "("; returned += "(";
returned += returnValues.join(", "); returned += returnValues.join(", ");
returned += ")"; returned += ")";
bytes data = tr.inputParameters;
data.erase(data.begin(), data.begin() + 4);
QStringList parameters = encoder.decode(funcDef->parametersList(), data);
for (int k = 0; k < parameters.length(); ++k)
inputParameters.insert(funcDef->parametersList().at(k)->name(), parameters.at(k));
} }
} }
// Fill generated logs and decode parameters
for (auto const& log: tr.logs)
{
QVariantMap l;
l.insert("address", QString::fromStdString(log.address.hex()));
std::ostringstream s;
s << log.data;
l.insert("data", QString::fromStdString(s.str()));
std::ostringstream streamTopic;
streamTopic << log.topics;
l.insert("topic", QString::fromStdString(streamTopic.str()));
auto const& sign = log.topics.front(); // first hash supposed to be the event signature. To check
auto dataIterator = log.data.begin();
int topicDataIndex = 1;
for (auto const& event: def->eventsList())
{
if (sign == event->fullHash())
{
QVariantList paramsList;
l.insert("name", event->name());
for (auto const& e: event->parametersList())
{
bytes data;
QString param;
if (!e->isIndexed())
{
data = bytes(dataIterator, dataIterator + 32);
dataIterator = dataIterator + 32;
}
else
{
data = log.topics.at(topicDataIndex).asBytes();
topicDataIndex++;
}
param = encoder.decode(e, data);
QVariantMap p;
p.insert("indexed", e->isIndexed());
p.insert("value", param);
p.insert("name", e->name());
paramsList.push_back(p);
}
l.insert("param", paramsList);
break;
}
}
logs.push_back(l);
}
}
QString sender;
for (auto const& secret: m_accountsSecret)
{
if (secret.address() == tr.sender)
{
sender = QString::fromStdString(dev::toHex(secret.secret().ref()));
break;
}
} }
QString label;
if (function != QObject::tr("<none>"))
label = contract + "." + function + "()";
else
label = contract;
if (!creation)
for (auto const& ctr: m_contractAddresses)
{
if (ctr.second == tr.address)
{
contract = "<" + ctr.first.first + " - " + QString::number(ctr.first.second) + ">";
break;
}
}
RecordLogEntry* log = new RecordLogEntry(recordIndex, transactionIndex, contract, function, value, address, returned, tr.isCall(), RecordLogEntry::RecordType::Transaction, gasUsed); RecordLogEntry* log = new RecordLogEntry(recordIndex, transactionIndex, contract, function, value, address, returned, tr.isCall(), RecordLogEntry::RecordType::Transaction,
gasUsed, sender, label, inputParameters, logs);
QQmlEngine::setObjectOwnership(log, QQmlEngine::JavaScriptOwnership); QQmlEngine::setObjectOwnership(log, QQmlEngine::JavaScriptOwnership);
emit newRecord(log); emit newRecord(log);
} }

41
mix/ClientModel.h

@ -30,12 +30,13 @@
#include <QVariantMap> #include <QVariantMap>
#include <QFuture> #include <QFuture>
#include <QVariableDeclaration.h> #include <QVariableDeclaration.h>
#include <libethereum/Account.h>
#include "MachineStates.h" #include "MachineStates.h"
namespace dev namespace dev
{ {
namespace eth { class Account; class FixedAccountHolder; } namespace eth { class FixedAccountHolder; }
namespace mix namespace mix
{ {
@ -108,6 +109,14 @@ class RecordLogEntry: public QObject
Q_PROPERTY(RecordType type MEMBER m_type CONSTANT) Q_PROPERTY(RecordType type MEMBER m_type CONSTANT)
/// Gas used /// Gas used
Q_PROPERTY(QString gasUsed MEMBER m_gasUsed CONSTANT) Q_PROPERTY(QString gasUsed MEMBER m_gasUsed CONSTANT)
/// Sender
Q_PROPERTY(QString sender MEMBER m_sender CONSTANT)
/// label
Q_PROPERTY(QString label MEMBER m_label CONSTANT)
/// input parameters
Q_PROPERTY(QVariantMap parameters MEMBER m_inputParameters CONSTANT)
/// logs
Q_PROPERTY(QVariantList logs MEMBER m_logs CONSTANT)
public: public:
enum RecordType enum RecordType
@ -118,8 +127,10 @@ public:
RecordLogEntry(): RecordLogEntry():
m_recordIndex(0), m_call(false), m_type(RecordType::Transaction) {} m_recordIndex(0), m_call(false), m_type(RecordType::Transaction) {}
RecordLogEntry(unsigned _recordIndex, QString _transactionIndex, QString _contract, QString _function, QString _value, QString _address, QString _returned, bool _call, RecordType _type, QString _gasUsed): RecordLogEntry(unsigned _recordIndex, QString _transactionIndex, QString _contract, QString _function, QString _value, QString _address, QString _returned, bool _call, RecordType _type, QString _gasUsed,
m_recordIndex(_recordIndex), m_transactionIndex(_transactionIndex), m_contract(_contract), m_function(_function), m_value(_value), m_address(_address), m_returned(_returned), m_call(_call), m_type(_type), m_gasUsed(_gasUsed) {} QString _sender, QString _label, QVariantMap _inputParameters, QVariantList _logs):
m_recordIndex(_recordIndex), m_transactionIndex(_transactionIndex), m_contract(_contract), m_function(_function), m_value(_value), m_address(_address), m_returned(_returned), m_call(_call), m_type(_type), m_gasUsed(_gasUsed),
m_sender(_sender), m_label(_label), m_inputParameters(_inputParameters), m_logs(_logs) {}
private: private:
unsigned m_recordIndex; unsigned m_recordIndex;
@ -132,6 +143,10 @@ private:
bool m_call; bool m_call;
RecordType m_type; RecordType m_type;
QString m_gasUsed; QString m_gasUsed;
QString m_sender;
QString m_label;
QVariantMap m_inputParameters;
QVariantList m_logs;
}; };
/** /**
@ -170,9 +185,11 @@ public:
Q_INVOKABLE QString toHex(QString const& _int); Q_INVOKABLE QString toHex(QString const& _int);
public slots: public slots:
/// Setup state, run transaction sequence, show debugger for the last transaction /// Setup scenario, run transaction sequence, show debugger for the last transaction
/// @param _state JS object with state configuration /// @param _state JS object with state configuration
void setupState(QVariantMap _state); void setupScenario(QVariantMap _scenario);
/// Execute the given @param _tr on the current state
void executeTr(QVariantMap _tr);
/// Show the debugger for a specified record /// Show the debugger for a specified record
Q_INVOKABLE void debugRecord(unsigned _index); Q_INVOKABLE void debugRecord(unsigned _index);
/// Show the debugger for an empty record /// Show the debugger for an empty record
@ -224,17 +241,21 @@ private:
RecordLogEntry* lastBlock() const; RecordLogEntry* lastBlock() const;
QVariantMap contractAddresses() const; QVariantMap contractAddresses() const;
QVariantList gasCosts() const; QVariantList gasCosts() const;
void executeSequence(std::vector<TransactionSettings> const& _sequence, std::unordered_map<Address, dev::eth::Account> const& _accounts, Secret const& _miner); void executeSequence(std::vector<TransactionSettings> const& _sequence);
dev::Address deployContract(bytes const& _code, TransactionSettings const& _tr = TransactionSettings()); dev::Address deployContract(bytes const& _code, TransactionSettings const& _tr = TransactionSettings());
void callAddress(Address const& _contract, bytes const& _data, TransactionSettings const& _tr); void callAddress(Address const& _contract, bytes const& _data, TransactionSettings const& _tr);
void onNewTransaction(); void onNewTransaction();
void onStateReset(); void onStateReset();
void showDebuggerForTransaction(ExecutionResult const& _t); void showDebuggerForTransaction(ExecutionResult const& _t);
QVariant formatValue(SolidityType const& _type, dev::u256 const& _value); QVariant formatValue(SolidityType const& _type, dev::u256 const& _value);
QString resolveToken(std::pair<QString, int> const& _value, std::vector<Address> const& _contracts); QString resolveToken(std::pair<QString, int> const& _value);
std::pair<QString, int> retrieveToken(QString const& _value, std::vector<Address> const& _contracts); std::pair<QString, int> retrieveToken(QString const& _value);
std::pair<QString, int> resolvePair(QString const& _contractId); std::pair<QString, int> resolvePair(QString const& _contractId);
QVariant formatStorageValue(SolidityType const& _type, std::unordered_map<dev::u256, dev::u256> const& _storage, unsigned _offset, dev::u256 const& _slot); QVariant formatStorageValue(SolidityType const& _type, std::unordered_map<dev::u256, dev::u256> const& _storage, unsigned _offset, dev::u256 const& _slot);
void processNextTransactions();
void finalizeBlock();
void stopExecution();
void setupExecutionChain();
std::atomic<bool> m_running; std::atomic<bool> m_running;
std::atomic<bool> m_mining; std::atomic<bool> m_mining;
@ -243,12 +264,16 @@ private:
std::unique_ptr<RpcConnector> m_rpcConnector; std::unique_ptr<RpcConnector> m_rpcConnector;
std::unique_ptr<Web3Server> m_web3Server; std::unique_ptr<Web3Server> m_web3Server;
std::shared_ptr<eth::FixedAccountHolder> m_ethAccounts; std::shared_ptr<eth::FixedAccountHolder> m_ethAccounts;
std::unordered_map<Address, eth::Account> m_accounts;
std::vector<KeyPair> m_accountsSecret;
QList<u256> m_gasCosts; QList<u256> m_gasCosts;
std::map<std::pair<QString, int>, Address> m_contractAddresses; std::map<std::pair<QString, int>, Address> m_contractAddresses;
std::map<Address, QString> m_contractNames; std::map<Address, QString> m_contractNames;
std::map<QString, Address> m_stdContractAddresses; std::map<QString, Address> m_stdContractAddresses;
std::map<Address, QString> m_stdContractNames; std::map<Address, QString> m_stdContractNames;
CodeModel* m_codeModel = nullptr; CodeModel* m_codeModel = nullptr;
QList<QVariantList> m_queueTransactions;
mutable boost::shared_mutex x_queueTransactions;
}; };
} }

7
mix/ContractCallDataEncoder.cpp

@ -29,6 +29,7 @@
#include "QVariableDefinition.h" #include "QVariableDefinition.h"
#include "QFunctionDefinition.h" #include "QFunctionDefinition.h"
#include "ContractCallDataEncoder.h" #include "ContractCallDataEncoder.h"
using namespace std;
using namespace dev; using namespace dev;
using namespace dev::solidity; using namespace dev::solidity;
using namespace dev::mix; using namespace dev::mix;
@ -227,6 +228,12 @@ QVariant ContractCallDataEncoder::decode(SolidityType const& _type, bytes const&
BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Parameter declaration not found")); BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Parameter declaration not found"));
} }
QString ContractCallDataEncoder::decode(QVariableDeclaration* const& _param, bytes _value)
{
SolidityType const& type = _param->type()->type();
return decode(type, _value).toString();
}
QStringList ContractCallDataEncoder::decode(QList<QVariableDeclaration*> const& _returnParameters, bytes _value) QStringList ContractCallDataEncoder::decode(QList<QVariableDeclaration*> const& _returnParameters, bytes _value)
{ {
bytesConstRef value(&_value); bytesConstRef value(&_value);

2
mix/ContractCallDataEncoder.h

@ -48,6 +48,8 @@ public:
void encode(QVariant const& _data, SolidityType const& _type); void encode(QVariant const& _data, SolidityType const& _type);
/// Decode variable in order to be sent to QML view. /// Decode variable in order to be sent to QML view.
QStringList decode(QList<QVariableDeclaration*> const& _dec, bytes _value); QStringList decode(QList<QVariableDeclaration*> const& _dec, bytes _value);
/// Decode @param _parameter
QString decode(QVariableDeclaration* const& _param, bytes _value);
/// Decode single variable /// Decode single variable
QVariant decode(SolidityType const& _type, bytes const& _value); QVariant decode(SolidityType const& _type, bytes const& _value);
/// Get all encoded data encoded by encode function. /// Get all encoded data encoded by encode function.

84
mix/MachineStates.h

@ -37,57 +37,59 @@ namespace dev
namespace mix namespace mix
{ {
/** /**
* @brief Store information about a machine state. * @brief Store information about a machine state.
*/ */
struct MachineState struct MachineState
{ {
uint64_t steps; uint64_t steps;
dev::u256 curPC; dev::u256 curPC;
dev::eth::Instruction inst; dev::eth::Instruction inst;
dev::bigint newMemSize; dev::bigint newMemSize;
dev::u256 gas; dev::u256 gas;
dev::u256s stack; dev::u256s stack;
dev::bytes memory; dev::bytes memory;
dev::bigint gasCost; dev::bigint gasCost;
std::unordered_map<dev::u256, dev::u256> storage; std::unordered_map<dev::u256, dev::u256> storage;
std::vector<unsigned> levels; std::vector<unsigned> levels;
unsigned codeIndex; unsigned codeIndex;
unsigned dataIndex; unsigned dataIndex;
}; };
/** /**
* @brief Executed conract code info * @brief Executed conract code info
*/ */
struct MachineCode struct MachineCode
{ {
dev::Address address; dev::Address address;
bytes code; bytes code;
}; };
/** /**
* @brief Store information about a machine states. * @brief Store information about a machine states.
*/ */
struct ExecutionResult struct ExecutionResult
{ {
ExecutionResult(): transactionIndex(std::numeric_limits<unsigned>::max()) {} ExecutionResult(): transactionIndex(std::numeric_limits<unsigned>::max()) {}
std::vector<MachineState> machineStates; std::vector<MachineState> machineStates;
std::vector<bytes> transactionData; std::vector<bytes> transactionData;
std::vector<MachineCode> executionCode; std::vector<MachineCode> executionCode;
dev::eth::ExecutionResult result; dev::eth::ExecutionResult result;
dev::Address address; dev::Address address;
dev::Address sender; dev::Address sender;
dev::Address contractAddress; dev::Address contractAddress;
dev::u256 value; dev::u256 value;
dev::u256 gasUsed; dev::u256 gasUsed;
unsigned transactionIndex; unsigned transactionIndex;
unsigned executonIndex = 0; unsigned executonIndex = 0;
bytes inputParameters;
eth::LocalisedLogEntries logs;
bool isCall() const { return transactionIndex == std::numeric_limits<unsigned>::max(); } bool isCall() const { return transactionIndex == std::numeric_limits<unsigned>::max(); }
bool isConstructor() const { return !isCall() && !address; } bool isConstructor() const { return !isCall() && !address; }
}; };
using ExecutionResults = std::vector<ExecutionResult>; using ExecutionResults = std::vector<ExecutionResult>;
} }
} }

95
mix/MixClient.cpp

@ -22,6 +22,7 @@
#include "MixClient.h" #include "MixClient.h"
#include <vector> #include <vector>
#include <utility>
#include <libdevcore/Exceptions.h> #include <libdevcore/Exceptions.h>
#include <libethereum/CanonBlockChain.h> #include <libethereum/CanonBlockChain.h>
#include <libethereum/Transaction.h> #include <libethereum/Transaction.h>
@ -58,9 +59,9 @@ bytes MixBlockChain::createGenesisBlock(h256 _stateRoot)
{ {
RLPStream block(3); RLPStream block(3);
block.appendList(15) block.appendList(15)
<< h256() << EmptyListSHA3 << h160() << _stateRoot << EmptyTrie << EmptyTrie << h256() << EmptyListSHA3 << h160() << _stateRoot << EmptyTrie << EmptyTrie
<< LogBloom() << c_mixGenesisDifficulty << 0 << c_genesisGasLimit << 0 << (unsigned)0 << LogBloom() << c_mixGenesisDifficulty << 0 << c_genesisGasLimit << 0 << (unsigned)0
<< std::string() << h256() << h64(u64(42)); << std::string() << h256() << h64(u64(42));
block.appendRaw(RLPEmptyList); block.appendRaw(RLPEmptyList);
block.appendRaw(RLPEmptyList); block.appendRaw(RLPEmptyList);
return block.out(); return block.out();
@ -78,8 +79,10 @@ MixClient::~MixClient()
void MixClient::resetState(std::unordered_map<Address, Account> const& _accounts, Secret const& _miner) void MixClient::resetState(std::unordered_map<Address, Account> const& _accounts, Secret const& _miner)
{ {
WriteGuard l(x_state); WriteGuard l(x_state);
Guard fl(x_filtersWatches); Guard fl(x_filtersWatches);
m_filters.clear(); m_filters.clear();
for (auto& i: m_specialFilters) for (auto& i: m_specialFilters)
i.second.clear(); i.second.clear();
@ -185,19 +188,19 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c
levels.resize(ext.depth); levels.resize(ext.depth);
machineStates.push_back(MachineState{ machineStates.push_back(MachineState{
steps, steps,
vm.curPC(), vm.curPC(),
inst, inst,
newMemSize, newMemSize,
static_cast<u256>(gas), static_cast<u256>(gas),
vm.stack(), vm.stack(),
vm.memory(), vm.memory(),
gasCost, gasCost,
ext.state().storage(ext.myAddress), ext.state().storage(ext.myAddress),
std::move(levels), std::move(levels),
codeIndex, codeIndex,
dataIndex dataIndex
}); });
}; };
execution.go(onOp); execution.go(onOp);
@ -215,14 +218,15 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c
BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Not enough gas")); BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Not enough gas"));
case TransactionException::BlockGasLimitReached: case TransactionException::BlockGasLimitReached:
BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Block gas limit reached")); BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Block gas limit reached"));
case TransactionException::BadJumpDestination:
BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Solidity exception (bad jump)"));
case TransactionException::OutOfStack: case TransactionException::OutOfStack:
BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Out of stack")); BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Out of stack"));
case TransactionException::StackUnderflow: case TransactionException::StackUnderflow:
BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Stack underflow")); BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Stack underflow"));
//these should not happen in mix //these should not happen in mix
case TransactionException::Unknown: case TransactionException::Unknown:
case TransactionException::BadInstruction: case TransactionException::BadInstruction:
case TransactionException::BadJumpDestination:
case TransactionException::InvalidSignature: case TransactionException::InvalidSignature:
case TransactionException::InvalidNonce: case TransactionException::InvalidNonce:
case TransactionException::BadRLP: case TransactionException::BadRLP:
@ -230,6 +234,7 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c
} }
ExecutionResult d; ExecutionResult d;
d.inputParameters = t.data();
d.result = er; d.result = er;
d.machineStates = machineStates; d.machineStates = machineStates;
d.executionCode = std::move(codes); d.executionCode = std::move(codes);
@ -248,29 +253,19 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c
if (!_call) if (!_call)
{ {
t = _gasAuto ? replaceGas(_t, d.gasUsed, _secret) : _t; t = _gasAuto ? replaceGas(_t, d.gasUsed, _secret) : _t;
er =_state.execute(lastHashes, t); er = _state.execute(lastHashes, t);
if (t.isCreation() && _state.code(d.contractAddress).empty()) if (t.isCreation() && _state.code(d.contractAddress).empty())
BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Not enough gas for contract deployment")); BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Not enough gas for contract deployment"));
d.gasUsed = er.gasUsed + er.gasRefunded + er.gasForDeposit + c_callStipend; d.gasUsed = er.gasUsed + er.gasRefunded + er.gasForDeposit + c_callStipend;
// collect watches LocalisedLogEntries logs;
h256Set changed; TransactionReceipt const& tr = _state.receipt(_state.pending().size() - 1);
Guard l(x_filtersWatches);
for (std::pair<h256 const, eth::InstalledFilter>& i: m_filters) //auto trHash = _state.pending().at(_state.pending().size() - 1).sha3();
if (compareBlockHashes(i.second.filter.latest(), bc().currentHash()) > 0) LogEntries le = tr.log();
{ if (le.size())
// acceptable number. for (unsigned j = 0; j < le.size(); ++j)
auto m = i.second.filter.matches(_state.receipt(_state.pending().size() - 1)); logs.insert(logs.begin(), LocalisedLogEntry(le[j]));
if (m.size()) d.logs = logs;
{
// filter catches them
for (LogEntry const& l: m)
i.second.changes.push_back(LocalisedLogEntry(l));
changed.insert(i.first);
}
}
changed.insert(dev::eth::PendingChangedFilter);
m_specialFilters.at(dev::eth::PendingChangedFilter).push_back(t.sha3());
noteChanged(changed);
} }
WriteGuard l(x_executions); WriteGuard l(x_executions);
m_executions.emplace_back(std::move(d)); m_executions.emplace_back(std::move(d));
@ -284,8 +279,7 @@ void MixClient::mine()
bc().import(m_state.blockData(), m_state.db(), ImportRequirements::Default & ~ImportRequirements::ValidNonce); bc().import(m_state.blockData(), m_state.db(), ImportRequirements::Default & ~ImportRequirements::ValidNonce);
m_state.sync(bc()); m_state.sync(bc());
m_startState = m_state; m_startState = m_state;
h256Set changed { dev::eth::ChainChangedFilter }; h256Set changed { dev::eth::PendingChangedFilter, dev::eth::ChainChangedFilter };
noteChanged(changed);
} }
ExecutionResult MixClient::lastExecution() const ExecutionResult MixClient::lastExecution() const
@ -303,7 +297,9 @@ ExecutionResult MixClient::execution(unsigned _index) const
State MixClient::asOf(h256 const& _block) const State MixClient::asOf(h256 const& _block) const
{ {
ReadGuard l(x_state); ReadGuard l(x_state);
return State(m_stateDB, bc(), _block); State ret(m_stateDB);
ret.populateFromChain(bc(), _block);
return ret;
} }
void MixClient::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, bool _gasAuto) void MixClient::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, bool _gasAuto)
@ -372,23 +368,6 @@ dev::eth::ExecutionResult MixClient::create(Address const& _from, u256 _value, b
return lastExecution().result; return lastExecution().result;
} }
void MixClient::noteChanged(h256Set const& _filters)
{
for (auto& i: m_watches)
if (_filters.count(i.second.id))
{
if (m_filters.count(i.second.id))
i.second.changes += m_filters.at(i.second.id).changes;
else if (m_specialFilters.count(i.second.id))
for (h256 const& hash: m_specialFilters.at(i.second.id))
i.second.changes.push_back(LocalisedLogEntry(SpecialLogEntry, hash));
}
for (auto& i: m_filters)
i.second.changes.clear();
for (auto& i: m_specialFilters)
i.second.clear();
}
eth::BlockInfo MixClient::blockInfo() const eth::BlockInfo MixClient::blockInfo() const
{ {
ReadGuard l(x_state); ReadGuard l(x_state);

2
mix/MixClient.h

@ -25,6 +25,7 @@
#include <vector> #include <vector>
#include <string> #include <string>
#include <libethereum/ExtVM.h>
#include <libethereum/ClientBase.h> #include <libethereum/ClientBase.h>
#include <libethereum/Client.h> #include <libethereum/Client.h>
#include "MachineStates.h" #include "MachineStates.h"
@ -88,7 +89,6 @@ protected:
private: private:
void executeTransaction(dev::eth::Transaction const& _t, eth::State& _state, bool _call, bool _gasAuto, dev::Secret const& _secret = dev::Secret()); void executeTransaction(dev::eth::Transaction const& _t, eth::State& _state, bool _call, bool _gasAuto, dev::Secret const& _secret = dev::Secret());
void noteChanged(h256Set const& _filters);
dev::eth::Transaction replaceGas(dev::eth::Transaction const& _t, dev::u256 const& _gas, dev::Secret const& _secret = dev::Secret()); dev::eth::Transaction replaceGas(dev::eth::Transaction const& _t, dev::u256 const& _gas, dev::Secret const& _secret = dev::Secret());
eth::State m_state; eth::State m_state;

17
mix/QContractDefinition.cpp

@ -39,8 +39,23 @@ QContractDefinition::QContractDefinition(QObject* _parent, dev::solidity::Contra
else else
m_constructor = new QFunctionDefinition(parent); m_constructor = new QFunctionDefinition(parent);
std::vector<std::string> found;
for (auto const& f: _contract->getDefinedFunctions())
{
m_functions.append(new QFunctionDefinition(parent, f));
found.push_back(f->getName());
}
for (auto const& it: _contract->getInterfaceFunctions()) for (auto const& it: _contract->getInterfaceFunctions())
m_functions.append(new QFunctionDefinition(parent, it.second)); {
if (std::find(found.begin(), found.end(), it.second->getDeclaration().getName()) == found.end())
m_functions.append(new QFunctionDefinition(parent, it.second));
}
for (auto const& it: _contract->getEvents())
m_events.append(new QFunctionDefinition(parent, it));
} }
QFunctionDefinition const* QContractDefinition::getFunction(dev::FixedHash<4> _hash) const QFunctionDefinition const* QContractDefinition::getFunction(dev::FixedHash<4> _hash) const

8
mix/QContractDefinition.h

@ -37,6 +37,7 @@ class QContractDefinition: public QBasicNodeDefinition
Q_OBJECT Q_OBJECT
Q_PROPERTY(QQmlListProperty<dev::mix::QFunctionDefinition> functions READ functions CONSTANT) Q_PROPERTY(QQmlListProperty<dev::mix::QFunctionDefinition> functions READ functions CONSTANT)
Q_PROPERTY(dev::mix::QFunctionDefinition* constructor READ constructor CONSTANT) Q_PROPERTY(dev::mix::QFunctionDefinition* constructor READ constructor CONSTANT)
Q_PROPERTY(QQmlListProperty<dev::mix::QFunctionDefinition> events READ events CONSTANT)
public: public:
QContractDefinition(QObject* _parent, solidity::ContractDefinition const* _contract); QContractDefinition(QObject* _parent, solidity::ContractDefinition const* _contract);
@ -44,12 +45,19 @@ public:
QQmlListProperty<QFunctionDefinition> functions() const { return QQmlListProperty<QFunctionDefinition>(const_cast<QContractDefinition*>(this), const_cast<QContractDefinition*>(this)->m_functions); } QQmlListProperty<QFunctionDefinition> functions() const { return QQmlListProperty<QFunctionDefinition>(const_cast<QContractDefinition*>(this), const_cast<QContractDefinition*>(this)->m_functions); }
/// Get the constructor of the contract. /// Get the constructor of the contract.
QFunctionDefinition* constructor() const { return m_constructor; } QFunctionDefinition* constructor() const { return m_constructor; }
/// Get all the functions of the contract.
QList<QFunctionDefinition*> const& functionsList() const { return m_functions; } QList<QFunctionDefinition*> const& functionsList() const { return m_functions; }
/// Find function by hash, returns nullptr if not found /// Find function by hash, returns nullptr if not found
QFunctionDefinition const* getFunction(dev::FixedHash<4> _hash) const; QFunctionDefinition const* getFunction(dev::FixedHash<4> _hash) const;
/// Get events
QQmlListProperty<QFunctionDefinition> events() const { return QQmlListProperty<QFunctionDefinition>(const_cast<QContractDefinition*>(this), const_cast<QContractDefinition*>(this)->m_events); }
/// Get events
QList<QFunctionDefinition*> const& eventsList() const { return m_events; }
private: private:
QList<QFunctionDefinition*> m_functions; QList<QFunctionDefinition*> m_functions;
QFunctionDefinition* m_constructor; QFunctionDefinition* m_constructor;
QList<QFunctionDefinition*> m_events;
}; };
} }

31
mix/QFunctionDefinition.cpp

@ -28,15 +28,40 @@
using namespace dev::solidity; using namespace dev::solidity;
using namespace dev::mix; using namespace dev::mix;
QFunctionDefinition::QFunctionDefinition(QObject* _parent, dev::solidity::FunctionTypePointer const& _f): QBasicNodeDefinition(_parent, &_f->getDeclaration()), m_hash(dev::sha3(_f->externalSignature())) QFunctionDefinition::QFunctionDefinition(QObject* _parent, dev::solidity::FunctionTypePointer const& _f): QBasicNodeDefinition(_parent, &_f->getDeclaration()), m_hash(dev::sha3(_f->externalSignature())),
m_fullHash(dev::sha3(_f->externalSignature()))
{
init(_f);
}
QFunctionDefinition::QFunctionDefinition(QObject* _parent, ASTPointer<FunctionDefinition> const& _f): QBasicNodeDefinition(_parent, _f.get()), m_hash(dev::sha3(_f->externalSignature())),
m_fullHash(dev::sha3(_f->externalSignature()))
{
for (unsigned i = 0; i < _f->getParameters().size(); ++i)
m_parameters.append(new QVariableDeclaration(parent(), _f->getParameters().at(i)));
for (unsigned i = 0; i < _f->getReturnParameters().size(); ++i)
m_returnParameters.append(new QVariableDeclaration(parent(), _f->getReturnParameters().at(i)));
}
QFunctionDefinition::QFunctionDefinition(QObject* _parent, ASTPointer<dev::solidity::EventDefinition> const& _e): QBasicNodeDefinition(_parent, _e.get())
{
for (unsigned i = 0; i < _e->getParameters().size(); ++i)
m_parameters.append(new QVariableDeclaration(parent(), _e->getParameters().at(i)));
FunctionTypePointer _f = std::make_shared<FunctionType>(*_e);
m_hash = (FixedHash<4>)dev::sha3(_f->externalSignature(_e->getName()));
m_fullHash = dev::sha3(_f->externalSignature(_e->getName()));
}
void QFunctionDefinition::init(dev::solidity::FunctionTypePointer _f)
{ {
auto paramNames = _f->getParameterNames(); auto paramNames = _f->getParameterNames();
auto paramTypes = _f->getParameterTypes(); auto paramTypes = _f->getParameterTypes();
auto returnNames = _f->getReturnParameterNames(); auto returnNames = _f->getReturnParameterNames();
auto returnTypes = _f->getReturnParameterTypes(); auto returnTypes = _f->getReturnParameterTypes();
for (unsigned i = 0; i < paramNames.size(); ++i) for (unsigned i = 0; i < paramNames.size(); ++i)
m_parameters.append(new QVariableDeclaration(_parent, paramNames[i], paramTypes[i].get())); m_parameters.append(new QVariableDeclaration(parent(), paramNames[i], paramTypes[i].get()));
for (unsigned i = 0; i < returnNames.size(); ++i) for (unsigned i = 0; i < returnNames.size(); ++i)
m_returnParameters.append(new QVariableDeclaration(_parent, returnNames[i], returnTypes[i].get())); m_returnParameters.append(new QVariableDeclaration(parent(), returnNames[i], returnTypes[i].get()));
} }

7
mix/QFunctionDefinition.h

@ -41,6 +41,10 @@ public:
QFunctionDefinition(){} QFunctionDefinition(){}
QFunctionDefinition(QObject* _parent): QBasicNodeDefinition(_parent) {} QFunctionDefinition(QObject* _parent): QBasicNodeDefinition(_parent) {}
QFunctionDefinition(QObject* _parent, solidity::FunctionTypePointer const& _f); QFunctionDefinition(QObject* _parent, solidity::FunctionTypePointer const& _f);
QFunctionDefinition(QObject* _parent, solidity::ASTPointer<solidity::EventDefinition> const& _f);
QFunctionDefinition(QObject* _parent, solidity::ASTPointer<solidity::FunctionDefinition> const& _f);
/// Init members
void init(dev::solidity::FunctionTypePointer _f);
/// Get all input parameters of this function. /// Get all input parameters of this function.
QList<QVariableDeclaration*> const& parametersList() const { return m_parameters; } QList<QVariableDeclaration*> const& parametersList() const { return m_parameters; }
/// Get all input parameters of this function as QML property. /// Get all input parameters of this function as QML property.
@ -49,10 +53,13 @@ public:
QList<QVariableDeclaration*> returnParameters() const { return m_returnParameters; } QList<QVariableDeclaration*> returnParameters() const { return m_returnParameters; }
/// Get the hash of this function declaration on the contract ABI. /// Get the hash of this function declaration on the contract ABI.
FixedHash<4> hash() const { return m_hash; } FixedHash<4> hash() const { return m_hash; }
/// Get the full hash of this function declaration on the contract ABI.
FixedHash<32> fullHash() const { return m_fullHash; }
private: private:
int m_index; int m_index;
FixedHash<4> m_hash; FixedHash<4> m_hash;
FixedHash<32> m_fullHash;
QList<QVariableDeclaration*> m_parameters; QList<QVariableDeclaration*> m_parameters;
QList<QVariableDeclaration*> m_returnParameters; QList<QVariableDeclaration*> m_returnParameters;
void initQParameters(); void initQParameters();

16
mix/QVariableDeclaration.cpp

@ -24,26 +24,28 @@
#include <libsolidity/AST.h> #include <libsolidity/AST.h>
#include "CodeModel.h" #include "CodeModel.h"
using namespace solidity;
namespace dev namespace dev
{ {
namespace mix namespace mix
{ {
QVariableDeclaration::QVariableDeclaration(QObject* _parent, solidity::VariableDeclaration const* _v): QVariableDeclaration::QVariableDeclaration(QObject* _parent, ASTPointer<VariableDeclaration> const _v):
QBasicNodeDefinition(_parent, _v), QBasicNodeDefinition(_parent, _v.get()),
m_type(new QSolidityType(this, CodeModel::nodeType(_v->getType().get()))) m_type(new QSolidityType(this, CodeModel::nodeType(_v->getType().get()))), m_isIndexed(_v->isIndexed())
{ {
} }
QVariableDeclaration::QVariableDeclaration(QObject* _parent, std::string const& _name, SolidityType const& _type): QVariableDeclaration::QVariableDeclaration(QObject* _parent, std::string const& _name, SolidityType const& _type, bool _isIndexed):
QBasicNodeDefinition(_parent, _name), QBasicNodeDefinition(_parent, _name),
m_type(new QSolidityType(_parent, _type)) m_type(new QSolidityType(_parent, _type)), m_isIndexed(_isIndexed)
{ {
} }
QVariableDeclaration::QVariableDeclaration(QObject* _parent, std::string const& _name, solidity::Type const* _type): QVariableDeclaration::QVariableDeclaration(QObject* _parent, std::string const& _name, solidity::Type const* _type, bool _isIndexed):
QBasicNodeDefinition(_parent, _name), QBasicNodeDefinition(_parent, _name),
m_type(new QSolidityType(this, CodeModel::nodeType(_type))) m_type(new QSolidityType(this, CodeModel::nodeType(_type))), m_isIndexed(_isIndexed)
{ {
} }

9
mix/QVariableDeclaration.h

@ -21,6 +21,7 @@
#include <QDebug> #include <QDebug>
#include <QVariantList> #include <QVariantList>
#include <libsolidity/AST.h>
#include "QBasicNodeDefinition.h" #include "QBasicNodeDefinition.h"
#include "SolidityType.h" #include "SolidityType.h"
@ -82,14 +83,16 @@ class QVariableDeclaration: public QBasicNodeDefinition
public: public:
QVariableDeclaration() {} QVariableDeclaration() {}
QVariableDeclaration(QObject* _parent, solidity::VariableDeclaration const* _v); QVariableDeclaration(QObject* _parent, solidity::ASTPointer<solidity::VariableDeclaration> const _v);
QVariableDeclaration(QObject* _parent, std::string const& _name, SolidityType const& _type); QVariableDeclaration(QObject* _parent, std::string const& _name, SolidityType const& _type, bool _isIndexed = false);
QVariableDeclaration(QObject* _parent, std::string const& _name, solidity::Type const* _type); QVariableDeclaration(QObject* _parent, std::string const& _name, solidity::Type const* _type, bool _isIndexed = false);
QSolidityType* type() const { return m_type; } QSolidityType* type() const { return m_type; }
void setType(QSolidityType* _type) { m_type = _type; } void setType(QSolidityType* _type) { m_type = _type; }
bool isIndexed() { return m_isIndexed; }
private: private:
QSolidityType* m_type; QSolidityType* m_type;
bool m_isIndexed;
}; };

5
mix/qml.qrc

@ -64,5 +64,10 @@
<file>qml/js/ansi2html.js</file> <file>qml/js/ansi2html.js</file>
<file>qml/js/NetworkDeployment.js</file> <file>qml/js/NetworkDeployment.js</file>
<file>qml/js/InputValidator.js</file> <file>qml/js/InputValidator.js</file>
<file>qml/Block.qml</file>
<file>qml/BlockChain.qml</file>
<file>qml/ScenarioExecution.qml</file>
<file>qml/ScenarioLoader.qml</file>
<file>qml/ScenarioButton.qml</file>
</qresource> </qresource>
</RCC> </RCC>

15
mix/qml/Application.qml

@ -128,10 +128,8 @@ ApplicationWindow {
MenuSeparator {} MenuSeparator {}
MenuItem { action: toggleProjectNavigatorAction } MenuItem { action: toggleProjectNavigatorAction }
MenuItem { action: showHideRightPanelAction } MenuItem { action: showHideRightPanelAction }
MenuItem { action: toggleTransactionLogAction }
MenuItem { action: toggleWebPreviewAction } MenuItem { action: toggleWebPreviewAction }
MenuItem { action: toggleWebPreviewOrientationAction } MenuItem { action: toggleWebPreviewOrientationAction }
//MenuItem { action: toggleCallsInLog }
} }
} }
@ -211,8 +209,8 @@ ApplicationWindow {
id: toggleAssemblyDebuggingAction id: toggleAssemblyDebuggingAction
text: qsTr("Show VM Code") text: qsTr("Show VM Code")
shortcut: "Ctrl+Alt+V" shortcut: "Ctrl+Alt+V"
onTriggered: mainContent.rightPane.assemblyMode = !mainContent.rightPane.assemblyMode; onTriggered: mainContent.debuggerPanel.assemblyMode = !mainContent.debuggerPanel.assemblyMode;
checked: mainContent.rightPane.assemblyMode; checked: mainContent.debuggerPanel.assemblyMode;
enabled: true enabled: true
} }
@ -225,15 +223,6 @@ ApplicationWindow {
onTriggered: mainContent.toggleWebPreview(); onTriggered: mainContent.toggleWebPreview();
} }
Action {
id: toggleTransactionLogAction
text: qsTr("Show States and Transactions")
shortcut: "Alt+1"
checkable: true
checked: mainContent.rightPane.transactionLog.visible
onTriggered: mainContent.rightPane.transactionLog.visible = !mainContent.rightPane.transactionLog.visible
}
Action { Action {
id: toggleProjectNavigatorAction id: toggleProjectNavigatorAction
text: qsTr("Show Project Navigator") text: qsTr("Show Project Navigator")

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

Loading…
Cancel
Save