Browse Source

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

cl-refactor
CJentzsch 10 years ago
parent
commit
9a89997845
  1. 8
      CMakeLists.txt
  2. 1
      alethzero/DappLoader.cpp
  3. 3
      alethzero/DappLoader.h
  4. 7
      alethzero/Main.ui
  5. 21
      alethzero/MainWin.cpp
  6. 15
      alethzero/OurWebThreeStubServer.cpp
  7. 7
      alethzero/OurWebThreeStubServer.h
  8. 8
      appdmg.json.in
  9. BIN
      bg.png
  10. 55
      eth/main.cpp
  11. 117
      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. 2
      libdevcore/FixedHash.h
  17. 63
      libethash-cl/ethash_cl_miner.cpp
  18. 18
      libethash-cl/ethash_cl_miner.h
  19. 25
      libethcore/Common.h
  20. 13
      libethcore/Ethash.cpp
  21. 16
      libethcore/Ethash.h
  22. 5
      libethcore/EthashAux.cpp
  23. 1
      libethcore/EthashAux.h
  24. 10
      libethereum/BlockChain.cpp
  25. 2
      libethereum/BlockChain.h
  26. 22
      libethereum/Client.cpp
  27. 4
      libethereum/Client.h
  28. 17
      libethereum/ClientBase.cpp
  29. 25
      libethereum/CommonNet.h
  30. 5
      libethereum/DownloadMan.h
  31. 132
      libethereum/EthereumHost.cpp
  32. 22
      libethereum/EthereumHost.h
  33. 59
      libethereum/ExtVM.cpp
  34. 7
      libethereum/State.cpp
  35. 26
      libevmasm/CommonSubexpressionEliminator.cpp
  36. 5586
      libjsqrc/ethereumjs/dist/web3-light.js
  37. 2
      libjsqrc/ethereumjs/dist/web3-light.min.js
  38. 17
      libjsqrc/ethereumjs/dist/web3.js
  39. 4
      libjsqrc/ethereumjs/dist/web3.min.js
  40. 2
      libjsqrc/ethereumjs/gulpfile.js
  41. 13
      libjsqrc/ethereumjs/lib/web3.js
  42. 1
      libjsqrc/ethereumjs/lib/web3/property.js
  43. 11
      libp2p/Host.cpp
  44. 6
      libp2p/Host.h
  45. 30
      libp2p/RLPXFrameCoder.cpp
  46. 45
      libp2p/RLPXFrameCoder.h
  47. 0
      libp2p/RLPXSocket.cpp
  48. 56
      libp2p/RLPXSocket.h
  49. 6
      libp2p/RLPxHandshake.cpp
  50. 9
      libp2p/RLPxHandshake.h
  51. 35
      libp2p/Session.cpp
  52. 11
      libp2p/Session.h
  53. 3
      libsolidity/Compiler.cpp
  54. 40
      libweb3jsonrpc/WebThreeStubServer.cpp
  55. 22
      libweb3jsonrpc/WebThreeStubServer.h
  56. 77
      libweb3jsonrpc/WebThreeStubServerBase.cpp
  57. 27
      libweb3jsonrpc/WebThreeStubServerBase.h
  58. 138
      libweb3jsonrpc/abstractwebthreestubserver.h
  59. 27
      libweb3jsonrpc/spec.json
  60. 239
      mix/ClientModel.cpp
  61. 41
      mix/ClientModel.h
  62. 7
      mix/ContractCallDataEncoder.cpp
  63. 2
      mix/ContractCallDataEncoder.h
  64. 28
      mix/MachineStates.h
  65. 54
      mix/MixClient.cpp
  66. 2
      mix/MixClient.h
  67. 15
      mix/QContractDefinition.cpp
  68. 8
      mix/QContractDefinition.h
  69. 31
      mix/QFunctionDefinition.cpp
  70. 7
      mix/QFunctionDefinition.h
  71. 16
      mix/QVariableDeclaration.cpp
  72. 9
      mix/QVariableDeclaration.h
  73. 5
      mix/qml.qrc
  74. 15
      mix/qml/Application.qml
  75. 346
      mix/qml/Block.qml
  76. 480
      mix/qml/BlockChain.qml
  77. 140
      mix/qml/Debugger.qml
  78. 60
      mix/qml/MainContent.qml
  79. 13
      mix/qml/QAddressView.qml
  80. 67
      mix/qml/ScenarioButton.qml
  81. 57
      mix/qml/ScenarioExecution.qml
  82. 175
      mix/qml/ScenarioLoader.qml
  83. 117
      mix/qml/StateListModel.qml
  84. 1
      mix/qml/StructView.qml
  85. 14
      mix/qml/TransactionDialog.qml
  86. BIN
      mix/qml/img/newIcon.png
  87. BIN
      mix/qml/img/newIcon@2x.png
  88. 2
      mix/qml/js/ProjectModel.js
  89. 3
      mix/qml/js/TransactionHelper.js
  90. 22
      mix/res.qrc
  91. 2
      test/TestHelper.cpp
  92. 13
      test/libevm/vm.cpp
  93. 8
      test/libsolidity/SolidityOptimizer.cpp
  94. 247
      test/libweb3jsonrpc/webthreestubclient.h

8
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")
@ -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;
}; };

7
alethzero/Main.ui

@ -45,6 +45,13 @@
</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">

21
alethzero/MainWin.cpp

@ -198,6 +198,7 @@ Main::Main(QWidget *parent) :
statusBar()->addPermanentWidget(ui->balance); statusBar()->addPermanentWidget(ui->balance);
statusBar()->addPermanentWidget(ui->peerCount); statusBar()->addPermanentWidget(ui->peerCount);
statusBar()->addPermanentWidget(ui->mineStatus); statusBar()->addPermanentWidget(ui->mineStatus);
statusBar()->addPermanentWidget(ui->syncStatus);
statusBar()->addPermanentWidget(ui->chainStatus); statusBar()->addPermanentWidget(ui->chainStatus);
statusBar()->addPermanentWidget(ui->blockCount); statusBar()->addPermanentWidget(ui->blockCount);
@ -209,7 +210,9 @@ Main::Main(QWidget *parent) :
m_webThree.reset(new WebThreeDirect(string("AlethZero/v") + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), getDataDir(), WithExisting::Trust, {"eth"/*, "shh"*/}, p2p::NetworkPreferences(), network)); m_webThree.reset(new WebThreeDirect(string("AlethZero/v") + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), getDataDir(), WithExisting::Trust, {"eth"/*, "shh"*/}, p2p::NetworkPreferences(), network));
m_httpConnector.reset(new jsonrpc::HttpServer(SensibleHttpPort, "", "", dev::SensibleHttpThreads)); m_httpConnector.reset(new jsonrpc::HttpServer(SensibleHttpPort, "", "", dev::SensibleHttpThreads));
m_server.reset(new OurWebThreeStubServer(*m_httpConnector, *web3(), this)); auto w3ss = new OurWebThreeStubServer(*m_httpConnector, this);
m_server.reset(w3ss);
auto sessionKey = w3ss->newSession({true});
connect(&*m_server, SIGNAL(onNewId(QString)), SLOT(addNewId(QString))); connect(&*m_server, SIGNAL(onNewId(QString)), SLOT(addNewId(QString)));
m_server->setIdentities(keysAsVector(owned())); m_server->setIdentities(keysAsVector(owned()));
m_server->StartListening(); m_server->StartListening();
@ -226,12 +229,16 @@ Main::Main(QWidget *parent) :
m_dappHost.reset(new DappHost(8081)); m_dappHost.reset(new DappHost(8081));
m_dappLoader = new DappLoader(this, web3(), getNameReg()); m_dappLoader = new DappLoader(this, web3(), getNameReg());
m_dappLoader->setSessionKey(sessionKey);
connect(m_dappLoader, &DappLoader::dappReady, this, &Main::dappLoaded); connect(m_dappLoader, &DappLoader::dappReady, this, &Main::dappLoaded);
connect(m_dappLoader, &DappLoader::pageReady, this, &Main::pageLoaded); connect(m_dappLoader, &DappLoader::pageReady, this, &Main::pageLoaded);
// ui->webView->page()->settings()->setAttribute(QWebEngineSettings::DeveloperExtrasEnabled, true); // ui->webView->page()->settings()->setAttribute(QWebEngineSettings::DeveloperExtrasEnabled, true);
// QWebEngineInspector* inspector = new QWebEngineInspector(); // QWebEngineInspector* inspector = new QWebEngineInspector();
// inspector->setPage(page); // inspector->setPage(page);
setBeneficiary(*m_keyManager.accounts().begin()); setBeneficiary(*m_keyManager.accounts().begin());
ethereum()->setDefault(LatestBlock);
readSettings(); readSettings();
m_transact = new Transact(this, this); m_transact = new Transact(this, this);
@ -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()

15
alethzero/OurWebThreeStubServer.cpp

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

7
alethzero/OurWebThreeStubServer.h

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

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

55
eth/main.cpp

@ -112,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;
@ -128,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
@ -290,6 +292,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;
@ -601,6 +604,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")
@ -683,7 +688,7 @@ int main(int argc, char** argv)
VMFactory::setKind(jit ? VMKind::JIT : VMKind::Interpreter); VMFactory::setKind(jit ? VMKind::JIT : VMKind::Interpreter);
auto netPrefs = publicIP.empty() ? NetworkPreferences(listenIP ,listenPort, upnp) : NetworkPreferences(publicIP, listenIP ,listenPort, upnp); auto netPrefs = publicIP.empty() ? NetworkPreferences(listenIP ,listenPort, upnp) : NetworkPreferences(publicIP, listenIP ,listenPort, upnp);
auto nodesState = contents((dbPath.size() ? dbPath : getDataDir()) + "/network.rlp"); auto nodesState = contents((dbPath.size() ? dbPath : getDataDir()) + "/network.rlp");
std::string clientImplString = "++eth/" + clientName + "v" + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM) + (jit ? "/JIT" : ""); std::string clientImplString = "++eth/" + clientName + "v" + dev::Version + "-" + string(DEV_QUOTED(ETH_COMMIT_HASH)).substr(0, 8) + (ETH_CLEAN_REPO ? "" : "*") + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM) + (jit ? "/JIT" : "");
dev::WebThreeDirect web3( dev::WebThreeDirect web3(
clientImplString, clientImplString,
dbPath, dbPath,
@ -810,14 +815,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;
if (bootstrap || !remoteHost.empty())
{
web3.startNetwork(); web3.startNetwork();
cout << "Node ID: " << web3.enode() << endl; cout << "Node ID: " << web3.enode() << endl;
}
if (bootstrap) else
for (auto const& i: Host::pocHosts()) cout << "Networking disabled. To start, use netstart or pass -b or a remote host." << endl;
web3.requirePeer(i.first, i.second);
if (remoteHost.size())
web3.addNode(p2p::NodeId(), remoteHost + ":" + toString(remotePort));
#if ETH_JSONRPC || !ETH_TRUE #if ETH_JSONRPC || !ETH_TRUE
shared_ptr<WebThreeStubServer> jsonrpcServer; shared_ptr<WebThreeStubServer> jsonrpcServer;
@ -825,11 +830,22 @@ int main(int argc, char** argv)
if (jsonrpc > -1) if (jsonrpc > -1)
{ {
jsonrpcConnector = unique_ptr<jsonrpc::AbstractServerConnector>(new jsonrpc::HttpServer(jsonrpc, "", "", SensibleHttpThreads)); jsonrpcConnector = unique_ptr<jsonrpc::AbstractServerConnector>(new jsonrpc::HttpServer(jsonrpc, "", "", SensibleHttpThreads));
jsonrpcServer = shared_ptr<WebThreeStubServer>(new WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared<SimpleAccountHolder>([&](){return web3.ethereum();}, getAccountPassword, keyManager), vector<KeyPair>())); jsonrpcServer = shared_ptr<WebThreeStubServer>(new WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared<SimpleAccountHolder>([&](){return web3.ethereum();}, getAccountPassword, keyManager), vector<KeyPair>(), keyManager));
jsonrpcServer->StartListening(); jsonrpcServer->StartListening();
if (jsonAdmin.empty())
jsonAdmin = jsonrpcServer->newSession(SessionPermissions{true});
else
jsonrpcServer->addSession(jsonAdmin, SessionPermissions{true});
cout << "JSONRPC Admin Session Key: " << jsonAdmin << endl;
} }
#endif #endif
if (bootstrap)
for (auto const& i: Host::pocHosts())
web3.requirePeer(i.first, i.second);
if (!remoteHost.empty())
web3.addNode(p2p::NodeId(), remoteHost + ":" + toString(remotePort));
signal(SIGABRT, &sighandler); signal(SIGABRT, &sighandler);
signal(SIGTERM, &sighandler); signal(SIGTERM, &sighandler);
signal(SIGINT, &sighandler); signal(SIGINT, &sighandler);
@ -969,8 +985,13 @@ int main(int argc, char** argv)
if (jsonrpc < 0) if (jsonrpc < 0)
jsonrpc = SensibleHttpPort; jsonrpc = SensibleHttpPort;
jsonrpcConnector = unique_ptr<jsonrpc::AbstractServerConnector>(new jsonrpc::HttpServer(jsonrpc, "", "", SensibleHttpThreads)); jsonrpcConnector = unique_ptr<jsonrpc::AbstractServerConnector>(new jsonrpc::HttpServer(jsonrpc, "", "", SensibleHttpThreads));
jsonrpcServer = shared_ptr<WebThreeStubServer>(new WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared<SimpleAccountHolder>([&](){return web3.ethereum();}, getAccountPassword, keyManager), vector<KeyPair>())); jsonrpcServer = shared_ptr<WebThreeStubServer>(new WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared<SimpleAccountHolder>([&](){ return web3.ethereum(); }, getAccountPassword, keyManager), vector<KeyPair>(), keyManager));
jsonrpcServer->StartListening(); jsonrpcServer->StartListening();
if (jsonAdmin.empty())
jsonAdmin = jsonrpcServer->newSession(SessionPermissions{true});
else
jsonrpcServer->addSession(jsonAdmin, SessionPermissions{true});
cout << "JSONRPC Admin Session Key: " << jsonAdmin << endl;
} }
else if (cmd == "jsonstop") else if (cmd == "jsonstop")
{ {
@ -1506,6 +1527,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;

117
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")
{
if (!ProofOfWork::GPUMiner::configureGPU())
{
cout << "No GPU device with sufficient memory was found. Defaulting to CPU" << endl;
m_minerType = MinerType::CPU;
}
else
{
m_minerType = MinerType::GPU; m_minerType = MinerType::GPU;
miningThreads = 1; else if (arg == "--current-block" && i + 1 < argc)
} m_currentBlock = stol(argv[++i]);
}
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
; ;
} }
@ -441,7 +457,7 @@ private:
cnote << "Grabbing DAG for" << newSeedHash; cnote << "Grabbing DAG for" << newSeedHash;
if (!(dag = EthashAux::full(newSeedHash, true, [&](unsigned _pc){ cout << "\rCreating DAG. " << _pc << "% done..." << flush; return 0; }))) if (!(dag = EthashAux::full(newSeedHash, true, [&](unsigned _pc){ cout << "\rCreating DAG. " << _pc << "% done..." << flush; return 0; })))
BOOST_THROW_EXCEPTION(DAGCreationFailure()); BOOST_THROW_EXCEPTION(DAGCreationFailure());
if (precompute) if (m_precompute)
EthashAux::computeFull(sha3(newSeedHash), true); EthashAux::computeFull(sha3(newSeedHash), true);
if (hh != current.headerHash) if (hh != current.headerHash)
{ {
@ -490,22 +506,27 @@ private:
/// Mining options /// Mining options
MinerType m_minerType = MinerType::CPU; MinerType m_minerType = MinerType::CPU;
unsigned openclPlatform = 0; unsigned m_openclPlatform = 0;
unsigned openclDevice = 0; unsigned m_openclDevice = 0;
unsigned miningThreads = UINT_MAX; unsigned m_miningThreads = UINT_MAX;
unsigned dagChunks = 1; bool m_shouldListDevices = false;
bool m_clAllowCPU = false;
bool m_forceSingleChunk = false;
boost::optional<uint64_t> m_currentBlock;
// default value is 350MB of GPU memory for other stuff (windows system rendering, e.t.c.)
unsigned m_extraGPUMemory = 350000000;
/// DAG initialisation param. /// DAG initialisation param.
unsigned initDAG = 0; unsigned m_initDAG = 0;
/// Benchmarking params /// Benchmarking params
bool phoneHome = true; bool m_phoneHome = true;
unsigned benchmarkWarmup = 3; unsigned m_benchmarkWarmup = 3;
unsigned benchmarkTrial = 3; unsigned m_benchmarkTrial = 3;
unsigned benchmarkTrials = 5; unsigned m_benchmarkTrials = 5;
/// Farm params /// Farm params
string farmURL = "http://127.0.0.1:8545"; string m_farmURL = "http://127.0.0.1:8545";
unsigned farmRecheckPeriod = 500; unsigned m_farmRecheckPeriod = 500;
bool precompute = true; bool m_precompute = true;
}; };

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;

2
libdevcore/FixedHash.h

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

63
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.");
@ -269,15 +291,18 @@ bool ethash_cl_miner::init(
// 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);
if (result >= ETHASH_CL_MINIMUM_MEMORY) if (s_forceSingleChunk || result >= _dagSize)
{ {
m_dagChunksNum = 1; m_dagChunksNum = 1;
ETHCL_LOG("Using 1 big chunk. Max OpenCL allocateable memory is" << result); ETHCL_LOG(
((result <= _dagSize && s_forceSingleChunk) ? "Forcing single chunk. Good luck!\n" : "") <<
"Using 1 big chunk. Max OpenCL allocateable memory is " << result
);
} }
else else
{ {
m_dagChunksNum = 4; m_dagChunksNum = 4;
ETHCL_LOG("Using 4 chunks. Max OpenCL allocateable memory is" << result); 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)

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

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

13
libethcore/Ethash.cpp

@ -381,9 +381,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; }

22
libethereum/Client.cpp

@ -611,11 +611,24 @@ 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";
tie(ir.first, ir.second, m_syncBlockQueue) = m_bc.sync(m_bq, m_stateDB, rand() % 10 + 5); boost::timer t;
tie(ir.first, ir.second, m_syncBlockQueue) = m_bc.sync(m_bq, m_stateDB, m_syncAmount);
double elapsed = t.elapsed();
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()) if (ir.first.empty())
return; return;
onChainChanged(ir); onChainChanged(ir);
@ -688,6 +701,8 @@ void Client::onChainChanged(ImportRoute const& _ir)
// RESTART MINING // RESTART MINING
if (!m_bq.items().first)
{
bool preChanged = false; bool preChanged = false;
State newPreMine; State newPreMine;
DEV_READ_GUARDED(x_preMine) DEV_READ_GUARDED(x_preMine)
@ -724,6 +739,7 @@ void Client::onChainChanged(ImportRoute const& _ir)
// Quick hack for now - the TQ at this point already has the prior pending transactions in it; // 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". // we should resync with it manually until we are stricter about what constitutes "knowing".
onTransactionQueueReady(); onTransactionQueueReady();
}
noteChanged(changeds); noteChanged(changeds);
} }
@ -901,8 +917,8 @@ void Client::flushTransactions()
doWork(); doWork();
} }
HashChainStatus Client::hashChainStatus() const SyncStatus Client::syncStatus() const
{ {
auto h = m_host.lock(); auto h = m_host.lock();
return h ? h->status() : HashChainStatus { 0, 0, false }; return h ? h->status() : SyncStatus();
} }

4
libethereum/Client.h

@ -157,7 +157,7 @@ public:
/// Get some information on the block queue. /// Get some information on the block queue.
BlockQueueStatus blockQueueStatus() const { return m_bq.status(); } BlockQueueStatus blockQueueStatus() const { return m_bq.status(); }
/// Get some information on the block queue. /// Get some information on the block queue.
HashChainStatus hashChainStatus() const; SyncStatus syncStatus() const;
/// Get the block queue. /// Get the block queue.
BlockQueue const& blockQueue() const { return m_bq; } BlockQueue const& blockQueue() const { return m_bq; }
@ -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;

17
libethereum/ClientBase.cpp

@ -49,30 +49,17 @@ void ClientBase::submitTransaction(Secret _secret, u256 _value, Address _dest, b
{ {
prepareForTransaction(); prepareForTransaction();
auto a = toAddress(_secret);
u256 n = postMine().transactionsFrom(a);
cdebug << "submitTx: " << a << "postMine=" << n << "; tq=" << m_tq.maxNonce(a);
Transaction t(_value, _gasPrice, _gas, _dest, _data, _nonce, _secret); Transaction t(_value, _gasPrice, _gas, _dest, _data, _nonce, _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) void ClientBase::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice)
{ {
prepareForTransaction();
auto a = toAddress(_secret); auto a = toAddress(_secret);
u256 n = postMine().transactionsFrom(a); submitTransaction(_secret, _value, _dest, _data, _gas, _gasPrice, max<u256>(postMine().transactionsFrom(a), m_tq.maxNonce(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());
StructuredLogger::transactionReceived(t.sha3().abridged(), t.sender().abridged());
cnote << "New transaction " << t << "(maxNonce for sender" << a << "is" << m_tq.maxNonce(a) << ")";
} }
Address ClientBase::submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice) Address ClientBase::submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice)

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

132
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,6 +90,22 @@ 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();
@ -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();
} }
@ -247,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)
@ -266,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()
@ -301,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();
@ -367,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...";
continueSync(); m_syncingLatestHash = h256();
setState(SyncState::NewBlocks);
m_man.resetToChain(m_hashes);
m_hashes.clear();
m_hashMan.reset(m_chain.number() + 1);
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");
@ -426,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)
{ {
@ -454,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;
@ -468,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;
@ -482,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;
@ -527,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;
} }
} }
@ -543,6 +582,7 @@ void EthereumHost::onPeerNewBlock(EthereumPeer* _peer, RLP const& _r)
if (sync) if (sync)
continueSync(); continueSync();
} }
DEV_INVARIANT_CHECK;
} }
void EthereumHost::onPeerTransactions(EthereumPeer* _peer, RLP const& _r) void EthereumHost::onPeerTransactions(EthereumPeer* _peer, RLP const& _r)
@ -587,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)
{ {
@ -597,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))
{ {
@ -632,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
@ -646,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?
@ -679,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
@ -687,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
@ -733,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;
}

22
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,13 +156,12 @@ 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.
}; };

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

7
libethereum/State.cpp

@ -141,7 +141,12 @@ State::State(OverlayDB const& _db, BlockChain const& _bc, h256 _h, ImportRequire
// 2. Enact the block's transactions onto this state. // 2. Enact the block's transactions onto this state.
m_ourAddress = bi.coinbaseAddress; m_ourAddress = bi.coinbaseAddress;
enact(BlockChain::verifyBlock(b), _bc, _ir); boost::timer t;
auto vb = BlockChain::verifyBlock(b);
cnote << "verifyBlock:" << t.elapsed();
t.restart();
enact(vb, _bc, _ir);
cnote << "enact:" << t.elapsed();
} }
else else
{ {

26
libevmasm/CommonSubexpressionEliminator.cpp

@ -79,32 +79,44 @@ 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();
if (*m_breakingItem == AssemblyItem(Instruction::JUMPI))
{
AssemblyItem::JumpType jumpType = m_breakingItem->getJumpType(); AssemblyItem::JumpType jumpType = m_breakingItem->getJumpType();
Id condition = m_state.stackElement(m_state.stackHeight() - 1, location); Id condition = m_state.stackElement(m_state.stackHeight() - 1, location);
Id zero = m_state.expressionClasses().find(u256(0)); if (classes.knownNonZero(condition))
if (m_state.expressionClasses().knownToBeDifferent(condition, zero))
{ {
feedItem(AssemblyItem(Instruction::SWAP1, location), true); feedItem(AssemblyItem(Instruction::SWAP1, location), true);
feedItem(AssemblyItem(Instruction::POP, location), true); feedItem(AssemblyItem(Instruction::POP, location), true);
AssemblyItem item(Instruction::JUMP, location); AssemblyItem item(Instruction::JUMP, location);
item.setJumpType(jumpType); item.setJumpType(jumpType);
m_breakingItem = m_state.expressionClasses().storeItem(item); m_breakingItem = classes.storeItem(item);
return;
} }
Id negatedCondition = m_state.expressionClasses().find(Instruction::ISZERO, {condition}); else if (classes.knownZero(condition))
if (m_state.expressionClasses().knownToBeDifferent(negatedCondition, zero))
{ {
AssemblyItem it(Instruction::POP, location); AssemblyItem it(Instruction::POP, location);
feedItem(it, true); feedItem(it, true);
feedItem(it, true); feedItem(it, true);
m_breakingItem = nullptr; m_breakingItem = nullptr;
} }
}
else if (*m_breakingItem == AssemblyItem(Instruction::RETURN))
{
Id size = m_state.stackElement(m_state.stackHeight() - 1, location);
if (classes.knownZero(size))
{
feedItem(AssemblyItem(Instruction::POP, location), true);
feedItem(AssemblyItem(Instruction::POP, location), true);
AssemblyItem item(Instruction::STOP, location);
m_breakingItem = classes.storeItem(item);
}
}
} }
CSECodeGenerator::CSECodeGenerator( CSECodeGenerator::CSECodeGenerator(

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

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

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

11
libp2p/Host.cpp

@ -61,17 +61,22 @@ ReputationManager::ReputationManager()
void ReputationManager::noteRude(Session const& _s, std::string const& _sub) void ReputationManager::noteRude(Session const& _s, std::string const& _sub)
{ {
DEV_WRITE_GUARDED(x_nodes)
m_nodes[make_pair(_s.id(), _s.info().clientVersion)].subs[_sub].isRude = true; 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
{ {
DEV_READ_GUARDED(x_nodes)
{
auto nit = m_nodes.find(make_pair(_s.id(), _s.info().clientVersion)); auto nit = m_nodes.find(make_pair(_s.id(), _s.info().clientVersion));
if (nit == m_nodes.end()) if (nit == m_nodes.end())
return false; return false;
auto sit = nit->second.subs.find(_sub); auto sit = nit->second.subs.find(_sub);
bool ret = sit == nit->second.subs.end() ? false : sit->second.isRude; bool ret = sit == nit->second.subs.end() ? false : sit->second.isRude;
return _sub.empty() ? ret : (ret || isRude(_s)); return _sub.empty() ? ret : (ret || isRude(_s));
}
return false;
} }
Host::Host(std::string const& _clientVersion, NetworkPreferences const& _n, bytesConstRef _restoreNetwork): Host::Host(std::string const& _clientVersion, NetworkPreferences const& _n, bytesConstRef _restoreNetwork):
@ -191,7 +196,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 +216,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 +235,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);

6
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;
@ -99,6 +100,7 @@ public:
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 +198,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);

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

45
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 * Thread Safety
* Distinct Objects: Safe. * Distinct Objects: Safe.
* Shared objects: Unsafe. * 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> class RLPXFrameCoder
{
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
* Distinct Objects: Safe.
* Shared objects: Unsafe.
*/
class RLPXFrameIO
{ {
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);
@ -107,8 +80,6 @@ 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,8 +96,6 @@ 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.

3
libsolidity/Compiler.cpp

@ -298,6 +298,9 @@ 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
if (dataOffset == 0)
m_context << eth::Instruction::STOP;
else
m_context << u256(dataOffset) << u256(0) << eth::Instruction::RETURN; m_context << u256(dataOffset) << u256(0) << eth::Instruction::RETURN;
} }

40
libweb3jsonrpc/WebThreeStubServer.cpp

@ -24,18 +24,18 @@
// 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 <libethcore/KeyManager.h>
#include <libwebthree/WebThree.h>
#include "WebThreeStubServer.h" #include "WebThreeStubServer.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): WebThreeStubServer::WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, WebThreeDirect& _web3, shared_ptr<AccountHolder> const& _ethAccounts, std::vector<dev::KeyPair> const& _shhAccounts, KeyManager& _keyMan):
WebThreeStubServerBase(_conn, _ethAccounts, _shhAccounts), WebThreeStubServerBase(_conn, _ethAccounts, _shhAccounts),
m_web3(_web3) m_web3(_web3),
m_keyMan(_keyMan)
{ {
auto path = getDataDir() + "/.web3"; auto path = getDataDir() + "/.web3";
boost::filesystem::create_directories(path); boost::filesystem::create_directories(path);
@ -44,6 +44,36 @@ 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;
}
Json::Value WebThreeStubServer::admin_eth_blockQueueStatus(string const& _session)
{
Json::Value ret;
if (isAdmin(_session))
{
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;
}
std::string WebThreeStubServer::web3_clientVersion() std::string WebThreeStubServer::web3_clientVersion()
{ {
return m_web3.clientVersion(); return m_web3.clientVersion();

22
libweb3jsonrpc/WebThreeStubServer.h

@ -33,19 +33,33 @@
namespace dev namespace dev
{ {
class WebThreeDirect; class WebThreeDirect;
namespace eth
{
class KeyManager;
}
} }
struct SessionPermissions
{
bool admin;
};
/** /**
* @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);
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:
bool isAdmin(std::string const& _session) const override { auto it = m_sessions.find(_session); return it != m_sessions.end() && it->second.admin; }
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 +68,15 @@ 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);
virtual Json::Value admin_eth_blockQueueStatus(std::string const& _session);
private: private:
dev::WebThreeDirect& m_web3; dev::WebThreeDirect& m_web3;
dev::eth::KeyManager& m_keyMan;
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;
}; };

77
libweb3jsonrpc/WebThreeStubServerBase.cpp

@ -798,6 +798,83 @@ string WebThreeStubServerBase::eth_compileSerpent(string const& _source)
return res; return res;
} }
bool WebThreeStubServerBase::admin_web3_setVerbosity(int _v, string const& _session)
{
if (!isAdmin(_session))
return false;
g_logVerbosity = _v;
return true;
}
bool WebThreeStubServerBase::admin_net_start(std::string const& _session)
{
if (!isAdmin(_session))
return false;
network()->startNetwork();
return true;
}
bool WebThreeStubServerBase::admin_net_stop(std::string const& _session)
{
if (!isAdmin(_session))
return false;
network()->stopNetwork();
return true;
}
bool WebThreeStubServerBase::admin_net_connect(std::string const& _node, std::string const& _session)
{
if (!isAdmin(_session))
return false;
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 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;
}
Json::Value WebThreeStubServerBase::admin_net_peers(std::string const& _session)
{
if (!isAdmin(_session))
return false;
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)
{
if (!isAdmin(_session))
return false;
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

27
libweb3jsonrpc/WebThreeStubServerBase.h

@ -122,6 +122,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);
@ -136,10 +137,36 @@ public:
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 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 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_setSigningKey(std::string const& _uuidOrAddress, std::string const& _session) { (void)_uuidOrAddress; (void)_session; return false; }
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, std::string const& _txIndex, std::string const& _session) { (void)_blockNumberOrHash; (void)_txIndex; (void)_session; return Json::Value(); }
virtual Json::Value admin_eth_getReceiptByHashAndIndex(std::string const& _blockNumberOrHash, std::string const& _txIndex, std::string const& _session) { (void)_blockNumberOrHash; (void)_txIndex; (void)_session; return Json::Value(); }
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:
virtual bool isAdmin(std::string const& _session) const { (void)_session; 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_STRING,"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_STRING,"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].asString(), 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].asString(), 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, const std::string& param2, const std::string& param3) = 0;
virtual Json::Value admin_eth_getReceiptByHashAndIndex(const std::string& param1, const std::string& 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": ["", "", ""], "returns": {} },
{ "name": "admin_eth_getReceiptByHashAndIndex", "params": ["", "", ""], "returns": {} }
] ]

239
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);
}
m_ethAccounts->setAccounts(m_accountsSecret);
bool trToExecute = false;
for (auto const& b: blocks)
{
QVariantList transactions = b.toMap().value("transactions").toList();
m_queueTransactions.push_back(transactions);
trToExecute = transactions.size() > 0;
} }
for (auto const& c: stateContracts) m_client->resetState(m_accounts, Secret(_scenario.value("miner").toMap().value("secret").toString().toStdString()));
if (m_queueTransactions.count() > 0 && trToExecute)
{ {
QVariantMap contract = c.toMap(); setupExecutionChain();
Address address = Address(fromHex(contract.value("address").toString().toStdString())); processNextTransactions();
Account account(qvariant_cast<QEther*>(contract.value("balance"))->toU256Wei(), Account::ContractConception);
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;
} }
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,11 +403,24 @@ 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)
{ {
@ -393,22 +430,26 @@ std::pair<QString, int> ClientModel::resolvePair(QString const& _contractId)
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());
} }
if (_contractId.startsWith("0x"))
ret = std::make_pair(_contractId, -2);
return ret; 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;
} }
@ -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.

28
mix/MachineStates.h

@ -37,11 +37,11 @@ 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;
@ -54,22 +54,22 @@ namespace mix
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;
@ -83,11 +83,13 @@ namespace mix
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>;
} }
} }

54
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>
@ -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();
@ -230,6 +233,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 +252,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 +278,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
@ -372,23 +365,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;

15
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())
{
if (std::find(found.begin(), found.end(), it.second->getDeclaration().getName()) == found.end())
m_functions.append(new QFunctionDefinition(parent, it.second)); 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")

346
mix/qml/Block.qml

@ -0,0 +1,346 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.1
import QtQuick.Dialogs 1.1
import QtQuick.Layouts 1.1
import Qt.labs.settings 1.0
import "js/Debugger.js" as Debugger
import "js/ErrorLocationFormater.js" as ErrorLocationFormater
import "."
ColumnLayout
{
id: root
property variant transactions
property string status
property int number
property int blockWidth: Layout.preferredWidth - statusWidth - horizontalMargin
property int horizontalMargin: 10
property int trHeight: 30
spacing: 0
property int openedTr: 0
property int blockIndex
property variant scenario
function calculateHeight()
{
if (transactions)
{
if (index >= 0)
return 30 + 30 * transactions.count + openedTr
else
return 30
}
else
return 30
}
onOpenedTrChanged:
{
Layout.preferredHeight = calculateHeight()
height = calculateHeight()
}
RowLayout
{
Layout.preferredHeight: trHeight
Layout.preferredWidth: blockWidth
id: rowHeader
Rectangle
{
color: "#DEDCDC"
Layout.preferredWidth: blockWidth
Layout.preferredHeight: trHeight
radius: 4
anchors.left: parent.left
anchors.leftMargin: statusWidth + 5
Label {
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: horizontalMargin
text:
{
if (status === "mined")
return qsTr("BLOCK") + " " + number
else
return qsTr("BLOCK") + " pending"
}
}
}
}
Repeater // List of transactions
{
id: transactionRepeater
model: transactions
RowLayout
{
id: rowTransaction
Layout.preferredHeight: trHeight
function displayContent()
{
logsText.text = ""
if (index >= 0 && transactions.get(index).logs && transactions.get(index).logs.count)
{
for (var k = 0; k < transactions.get(index).logs.count; k++)
{
var log = transactions.get(index).logs.get(k)
if (log.name)
logsText.text += log.name + ":\n"
else
logsText.text += "log:\n"
if (log.param)
for (var i = 0; i < log.param.count; i++)
{
var p = log.param.get(i)
logsText.text += p.name + " = " + p.value + " - indexed:" + p.indexed + "\n"
}
else{
logsText.text += "From : " + log.address + "\n"
}
}
logsText.text += "\n\n"
}
rowDetailedContent.visible = !rowDetailedContent.visible
}
Rectangle
{
id: trSaveStatus
Layout.preferredWidth: statusWidth
Layout.preferredHeight: trHeight
color: "transparent"
anchors.top: parent.top
property bool saveStatus
Image {
id: saveStatusImage
source: "qrc:/qml/img/recyclediscard@2x.png"
width: statusWidth
fillMode: Image.PreserveAspectFit
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
}
Component.onCompleted:
{
if (index >= 0)
saveStatus = transactions.get(index).saveStatus
}
onSaveStatusChanged:
{
if (saveStatus)
saveStatusImage.source = "qrc:/qml/img/recyclekeep@2x.png"
else
saveStatusImage.source = "qrc:/qml/img/recyclediscard@2x.png"
if (index >= 0)
transactions.get(index).saveStatus = saveStatus
}
MouseArea {
id: statusMouseArea
anchors.fill: parent
onClicked:
{
parent.saveStatus = !parent.saveStatus
}
}
}
Rectangle
{
Layout.preferredWidth: blockWidth
Layout.preferredHeight: parent.height
color: "#DEDCDC"
id: rowContentTr
anchors.top: parent.top
ColumnLayout
{
anchors.top: parent.top
spacing: 10
RowLayout
{
anchors.top: parent.top
anchors.verticalCenter: parent.verticalCenter
spacing: cellSpacing
Text
{
id: hash
anchors.left: parent.left
anchors.leftMargin: horizontalMargin
Layout.preferredWidth: fromWidth
elide: Text.ElideRight
maximumLineCount: 1
text: {
if (index >= 0)
return transactions.get(index).sender
else
return ""
}
}
Text
{
id: func
text: {
if (index >= 0)
parent.userFrienldyToken(transactions.get(index).label)
else
return ""
}
elide: Text.ElideRight
maximumLineCount: 1
Layout.preferredWidth: toWidth
}
function userFrienldyToken(value)
{
if (value && value.indexOf("<") === 0)
{
if (value.split("> ")[1] === " - ")
return value.split(" - ")[0].replace("<", "")
else
return value.split(" - ")[0].replace("<", "") + "." + value.split("> ")[1] + "()";
}
else
return value
}
Text
{
id: returnValue
elide: Text.ElideRight
maximumLineCount: 1
Layout.preferredWidth: valueWidth
text: {
if (index >= 0 && transactions.get(index).returned)
return transactions.get(index).returned
else
return ""
}
}
Rectangle
{
Layout.preferredWidth: logsWidth
Layout.preferredHeight: trHeight - 10
width: logsWidth
color: "transparent"
Text
{
id: logs
anchors.left: parent.left
anchors.leftMargin: 10
text: {
if (index >= 0 && transactions.get(index).logs && transactions.get(index).logs.count)
return transactions.get(index).logs.count
else
return ""
}
}
MouseArea {
anchors.fill: parent
onClicked: {
rowTransaction.displayContent();
}
}
}
Rectangle
{
Layout.preferredWidth: debugActionWidth
Layout.preferredHeight: trHeight - 10
color: "transparent"
Image {
source: "qrc:/qml/img/edit.png"
width: 18
fillMode: Image.PreserveAspectFit
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
}
MouseArea
{
anchors.fill: parent
onClicked:
{
transactionDialog.stateAccounts = scenario.accounts
transactionDialog.execute = false
transactionDialog.open(index, blockIndex, transactions.get(index))
}
}
}
Rectangle
{
Layout.preferredWidth: debugActionWidth
Layout.preferredHeight: trHeight - 10
color: "transparent"
Image {
id: debugImg
source: "qrc:/qml/img/rightarrow@2x.png"
width: statusWidth
fillMode: Image.PreserveAspectFit
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
visible: transactions.get(index).recordIndex !== undefined
}
MouseArea
{
anchors.fill: parent
onClicked:
{
if (transactions.get(index).recordIndex !== undefined)
{
debugTrRequested = [ blockIndex, index ]
clientModel.debugRecord(transactions.get(index).recordIndex);
}
}
}
}
}
RowLayout
{
id: rowDetailedContent
visible: false
Layout.preferredHeight:{
if (index >= 0 && transactions.get(index).logs)
return 100 * transactions.get(index).logs.count
else
return 100
}
onVisibleChanged:
{
var lognb = transactions.get(index).logs.count
if (visible)
{
rowContentTr.Layout.preferredHeight = trHeight + 100 * lognb
openedTr += 100 * lognb
}
else
{
rowContentTr.Layout.preferredHeight = trHeight
openedTr -= 100 * lognb
}
}
Text {
anchors.left: parent.left
anchors.leftMargin: horizontalMargin
id: logsText
}
}
}
}
}
}
}

480
mix/qml/BlockChain.qml

@ -0,0 +1,480 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.1
import QtQuick.Dialogs 1.1
import QtQuick.Layouts 1.1
import Qt.labs.settings 1.0
import org.ethereum.qml.QEther 1.0
import "js/Debugger.js" as Debugger
import "js/ErrorLocationFormater.js" as ErrorLocationFormater
import "js/TransactionHelper.js" as TransactionHelper
import "js/QEtherHelper.js" as QEtherHelper
import "."
ColumnLayout {
id: blockChainPanel
property variant model
spacing: 0
property int previousWidth
property variant debugTrRequested: []
signal chainChanged
onChainChanged: {
reBuildNeeded.start()
}
onWidthChanged:
{
if (width <= 630 || previousWidth <= 630)
{
fromWidth = 100
toWidth = 100
valueWidth = 200
}
else
{
var diff = (width - previousWidth) / 3;
fromWidth = fromWidth + diff < 100 ? 100 : fromWidth + diff
toWidth = toWidth + diff < 100 ? 100 : toWidth + diff
valueWidth = valueWidth + diff < 200 ? 200 : valueWidth + diff
}
previousWidth = width
}
function load(scenario)
{
if (!scenario)
return;
if (model)
chainChanged()
model = scenario
blockModel.clear()
for (var b in model.blocks)
blockModel.append(model.blocks[b])
previousWidth = width
}
property int statusWidth: 30
property int fromWidth: 100
property int toWidth: 100
property int valueWidth: 200
property int logsWidth: 50
property int debugActionWidth: 50
property int horizontalMargin: 10
property int cellSpacing: 10
RowLayout
{
id: header
spacing: 0
Layout.preferredHeight: 25
Image {
id: debugImage
source: "qrc:/qml/img/recycleicon@2x.png"
Layout.preferredWidth: statusWidth
Layout.preferredHeight: 25
fillMode: Image.PreserveAspectFit
}
Rectangle
{
Layout.preferredWidth: fromWidth + cellSpacing
Label
{
anchors.verticalCenter: parent.verticalCenter
text: "From"
anchors.left: parent.left
anchors.leftMargin: horizontalMargin + 5
}
}
Label
{
text: "To"
Layout.preferredWidth: toWidth + cellSpacing
}
Label
{
text: "Value"
Layout.preferredWidth: valueWidth + cellSpacing
}
Label
{
text: "Logs"
Layout.preferredWidth: logsWidth + cellSpacing
}
Label
{
text: ""
Layout.preferredWidth: debugActionWidth
}
}
Rectangle
{
Layout.preferredHeight: 500
Layout.preferredWidth: parent.width
border.color: "#cccccc"
border.width: 2
color: "white"
ScrollView
{
id: blockChainScrollView
anchors.fill: parent
anchors.topMargin: 10
ColumnLayout
{
id: blockChainLayout
width: parent.width
spacing: 10
Repeater // List of blocks
{
id: blockChainRepeater
model: blockModel
Block
{
scenario: blockChainPanel.model
Layout.preferredWidth: blockChainScrollView.width
Layout.preferredHeight:
{
return calculateHeight()
}
blockIndex: index
transactions:
{
if (index >= 0)
return blockModel.get(index).transactions
else
return []
}
status:
{
if (index >= 0)
return blockModel.get(index).status
else
return ""
}
number:
{
if (index >= 0)
return blockModel.get(index).number
else
return 0
}
}
}
}
}
}
ListModel
{
id: blockModel
function appendBlock(block)
{
blockModel.append(block);
}
function appendTransaction(tr)
{
blockModel.get(blockModel.count - 1).transactions.append(tr)
}
function removeTransaction(blockIndex, trIndex)
{
blockModel.get(blockIndex).transactions.remove(trIndex)
}
function removeLastBlock()
{
blockModel.remove(blockModel.count - 1)
}
function removeBlock(index)
{
blockModel.remove(index)
}
function getTransaction(block, tr)
{
return blockModel.get(block).transactions.get(tr)
}
function setTransaction(blockIndex, trIndex, tr)
{
blockModel.get(blockIndex).transactions.set(trIndex, tr)
}
function setTransactionProperty(blockIndex, trIndex, propertyName, value)
{
blockModel.get(blockIndex).transactions.set(trIndex, { propertyName: value })
}
}
Rectangle
{
Layout.preferredWidth: parent.width
RowLayout
{
width: 4 * 100
anchors.top: parent.top
anchors.topMargin: 10
spacing: 0
ScenarioButton {
id: rebuild
text: qsTr("Rebuild")
onClicked:
{
if (ensureNotFuturetime.running)
return;
reBuildNeeded.stop()
var retBlocks = [];
var bAdded = 0;
for (var j = 0; j < model.blocks.length; j++)
{
var b = model.blocks[j];
var block = {
hash: b.hash,
number: b.number,
transactions: [],
status: b.status
}
for (var k = 0; k < model.blocks[j].transactions.length; k++)
{
if (blockModel.get(j).transactions.get(k).saveStatus)
{
var tr = model.blocks[j].transactions[k]
tr.saveStatus = true
block.transactions.push(tr);
}
}
if (block.transactions.length > 0)
{
bAdded++
block.number = bAdded
block.status = "mined"
retBlocks.push(block)
}
}
if (retBlocks.length === 0)
retBlocks.push(projectModel.stateListModel.createEmptyBlock())
else
{
var last = retBlocks[retBlocks.length - 1]
last.number = -1
last.status = "pending"
}
model.blocks = retBlocks
blockModel.clear()
for (var j = 0; j < model.blocks.length; j++)
blockModel.append(model.blocks[j])
ensureNotFuturetime.start()
clientModel.setupScenario(model);
}
Layout.preferredWidth: 100
Layout.preferredHeight: 30
buttonShortcut: ""
sourceImg: "qrc:/qml/img/recycleicon@2x.png"
Timer
{
id: reBuildNeeded
repeat: true
interval: 1000
running: false
onTriggered: {
if (!parent.fillColor || parent.fillColor === "white")
parent.fillColor = "orange"
else
parent.fillColor = "white"
}
onRunningChanged: {
if (!running)
parent.fillColor = "white"
}
}
}
ScenarioButton {
id: addTransaction
text: qsTr("Add Transaction")
onClicked:
{
var lastBlock = model.blocks[model.blocks.length - 1];
if (lastBlock.status === "mined")
{
var newblock = projectModel.stateListModel.createEmptyBlock()
blockModel.appendBlock(newblock)
model.blocks.push(newblock);
}
var item = TransactionHelper.defaultTransaction()
transactionDialog.stateAccounts = model.accounts
transactionDialog.execute = true
transactionDialog.open(model.blocks[model.blocks.length - 1].transactions.length, model.blocks.length - 1, item)
}
Layout.preferredWidth: 100
Layout.preferredHeight: 30
buttonShortcut: ""
sourceImg: "qrc:/qml/img/sendtransactionicon@2x.png"
}
Timer
{
id: ensureNotFuturetime
interval: 1000
repeat: false
running: false
}
ScenarioButton {
id: addBlockBtn
text: qsTr("Add Block")
onClicked:
{
if (ensureNotFuturetime.running)
return
if (clientModel.mining || clientModel.running)
return
if (model.blocks.length > 0)
{
var lastBlock = model.blocks[model.blocks.length - 1]
if (lastBlock.status === "pending")
{
ensureNotFuturetime.start()
clientModel.mine()
}
else
addNewBlock()
}
else
addNewBlock()
}
function addNewBlock()
{
var block = projectModel.stateListModel.createEmptyBlock()
model.blocks.push(block)
blockModel.appendBlock(block)
}
Layout.preferredWidth: 100
Layout.preferredHeight: 30
buttonShortcut: ""
sourceImg: "qrc:/qml/img/addblock@2x.png"
}
Connections
{
target: clientModel
onNewBlock:
{
if (!clientModel.running)
{
var lastBlock = model.blocks[model.blocks.length - 1]
lastBlock.status = "mined"
lastBlock.number = model.blocks.length
var lastB = blockModel.get(model.blocks.length - 1)
lastB.status = "mined"
lastB.number = model.blocks.length
addBlockBtn.addNewBlock()
}
}
onStateCleared:
{
}
onNewRecord:
{
var blockIndex = parseInt(_r.transactionIndex.split(":")[0]) - 1
var trIndex = parseInt(_r.transactionIndex.split(":")[1])
if (blockIndex <= model.blocks.length - 1)
{
var item = model.blocks[blockIndex]
if (trIndex <= item.transactions.length - 1)
{
var tr = item.transactions[trIndex]
tr.returned = _r.returned
tr.recordIndex = _r.recordIndex
tr.logs = _r.logs
tr.sender = _r.sender
var trModel = blockModel.getTransaction(blockIndex, trIndex)
trModel.returned = _r.returned
trModel.recordIndex = _r.recordIndex
trModel.logs = _r.logs
trModel.sender = _r.sender
blockModel.setTransaction(blockIndex, trIndex, trModel)
return;
}
}
// tr is not in the list.
var itemTr = TransactionHelper.defaultTransaction()
itemTr.saveStatus = false
itemTr.functionId = _r.function
itemTr.contractId = _r.contract
itemTr.gasAuto = true
itemTr.parameters = _r.parameters
itemTr.isContractCreation = itemTr.functionId === itemTr.contractId
itemTr.label = _r.label
itemTr.isFunctionCall = itemTr.functionId !== ""
itemTr.returned = _r.returned
itemTr.value = QEtherHelper.createEther(_r.value, QEther.Wei)
itemTr.sender = _r.sender
itemTr.recordIndex = _r.recordIndex
itemTr.logs = _r.logs
model.blocks[model.blocks.length - 1].transactions.push(itemTr)
blockModel.appendTransaction(itemTr)
}
onMiningComplete:
{
}
}
ScenarioButton {
id: newAccount
text: qsTr("New Account")
onClicked: {
model.accounts.push(projectModel.stateListModel.newAccount("1000000", QEther.Ether))
}
Layout.preferredWidth: 100
Layout.preferredHeight: 30
buttonShortcut: ""
sourceImg: "qrc:/qml/img/newaccounticon@2x.png"
}
}
}
TransactionDialog {
id: transactionDialog
property bool execute
onAccepted: {
var item = transactionDialog.getItem()
if (execute)
{
var lastBlock = model.blocks[model.blocks.length - 1];
if (lastBlock.status === "mined")
{
var newBlock = projectModel.stateListModel.createEmptyBlock();
model.blocks.push(newBlock);
blockModel.appendBlock(newBlock)
}
if (!clientModel.running)
clientModel.executeTr(item)
}
else {
model.blocks[blockIndex].transactions[transactionIndex] = item
blockModel.setTransaction(blockIndex, transactionIndex, item)
chainChanged()
}
}
}
}

140
mix/qml/Debugger.qml

@ -11,7 +11,6 @@ import "."
Rectangle { Rectangle {
id: debugPanel id: debugPanel
property alias transactionLog: transactionLog
property alias debugSlider: statesSlider property alias debugSlider: statesSlider
property alias solLocals: solLocals property alias solLocals: solLocals
property alias solStorage: solStorage property alias solStorage: solStorage
@ -23,7 +22,7 @@ Rectangle {
signal debugExecuteLocation(string documentId, var location) signal debugExecuteLocation(string documentId, var location)
property string compilationErrorMessage property string compilationErrorMessage
property bool assemblyMode: false property bool assemblyMode: false
signal panelClosed
objectName: "debugPanel" objectName: "debugPanel"
color: "#ededed" color: "#ededed"
clip: true clip: true
@ -40,6 +39,11 @@ Rectangle {
machineStates.updateHeight(); machineStates.updateHeight();
} }
function setTr(tr)
{
trName.text = tr.label
}
function displayCompilationErrorIfAny() function displayCompilationErrorIfAny()
{ {
debugScrollArea.visible = false; debugScrollArea.visible = false;
@ -61,7 +65,6 @@ Rectangle {
{ {
Debugger.init(data); Debugger.init(data);
debugScrollArea.visible = true; debugScrollArea.visible = true;
compilationErrorArea.visible = false;
machineStates.visible = true; machineStates.visible = true;
} }
if (giveFocus) if (giveFocus)
@ -97,97 +100,78 @@ Rectangle {
Settings { Settings {
id: splitSettings id: splitSettings
property alias transactionLogHeight: transactionLog.height
property alias callStackHeight: callStackRect.height property alias callStackHeight: callStackRect.height
property alias storageHeightSettings: storageRect.height property alias storageHeightSettings: storageRect.height
property alias memoryDumpHeightSettings: memoryRect.height property alias memoryDumpHeightSettings: memoryRect.height
property alias callDataHeightSettings: callDataRect.height property alias callDataHeightSettings: callDataRect.height
property alias transactionLogVisible: transactionLog.visible
property alias solCallStackHeightSettings: solStackRect.height property alias solCallStackHeightSettings: solStackRect.height
property alias solStorageHeightSettings: solStorageRect.height property alias solStorageHeightSettings: solStorageRect.height
property alias solLocalsHeightSettings: solLocalsRect.height property alias solLocalsHeightSettings: solLocalsRect.height
} }
Rectangle ColumnLayout {
{ id: debugScrollArea
visible: false; anchors.fill: parent
id: compilationErrorArea //orientation: Qt.Vertical
width: parent.width - 20 spacing: 0
height: 600 RowLayout
color: "#ededed"
anchors.left: parent.left
anchors.top: parent.top
anchors.margins: 10
ColumnLayout
{ {
width: parent.width Layout.preferredWidth: parent.width
anchors.top: parent.top Layout.preferredHeight: 30
spacing: 15
Rectangle Rectangle
{ {
height: 15 Layout.preferredWidth: parent.width
Button { Layout.preferredHeight: parent.height
text: qsTr("Back to Debugger") color: "transparent"
onClicked: { Text {
debugScrollArea.visible = true; anchors.centerIn: parent
compilationErrorArea.visible = false; text: qsTr("Current Transaction")
machineStates.visible = true;
}
}
} }
RowLayout Rectangle
{ {
height: 100 anchors.left: parent.left
ColumnLayout anchors.leftMargin: 10
width: 30
height: parent.height
color: "transparent"
anchors.verticalCenter: parent.verticalCenter
Image {
source: "qrc:/qml/img/leftarrow@2x.png"
width: parent.width
fillMode: Image.PreserveAspectFit
anchors.centerIn: parent
}
MouseArea
{ {
Text { anchors.fill: parent
color: "red" onClicked:
id: errorLocation {
Debugger.init(null);
panelClosed()
} }
Text {
color: "#4a4a4a"
id: errorDetail
} }
} }
} }
Rectangle
{
width: parent.width - 6
height: 2
color: "#d0d0d0"
} }
RowLayout RowLayout
{ {
Text Layout.preferredWidth: parent.width
Layout.preferredHeight: 30
Rectangle
{ {
color: "#4a4a4a" Layout.preferredWidth: parent.width
id: errorLine Layout.preferredHeight: parent.height
} color: "#2C79D3"
Text {
id: trName
color: "white"
anchors.centerIn: parent
} }
} }
} }
Splitter {
id: debugScrollArea
anchors.fill: parent
orientation: Qt.Vertical
TransactionLog {
id: transactionLog
Layout.fillWidth: true
Layout.minimumHeight: 130
height: 250
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: machineStates.sideMargin
anchors.rightMargin: machineStates.sideMargin
anchors.topMargin: machineStates.sideMargin
}
ScrollView ScrollView
{ {
property int sideMargin: 10 property int sideMargin: 10
@ -230,32 +214,6 @@ Rectangle {
spacing: 3 spacing: 3
layoutDirection: Qt.LeftToRight layoutDirection: Qt.LeftToRight
StepActionImage
{
id: playAction
enabledStateImg: "qrc:/qml/img/play_button.png"
disableStateImg: "qrc:/qml/img/play_button.png"
buttonLeft: true
onClicked: projectModel.stateListModel.runState(transactionLog.selectedStateIndex)
width: 23
buttonShortcut: "Ctrl+Shift+F8"
buttonTooltip: qsTr("Start Debugging")
visible: true
Layout.alignment: Qt.AlignLeft
}
StepActionImage
{
id: pauseAction
enabledStateImg: "qrc:/qml/img/stop_button2x.png"
disableStateImg: "qrc:/qml/img/stop_button2x.png"
onClicked: Debugger.init(null);
width: 23
buttonShortcut: "Ctrl+Shift+F9"
buttonTooltip: qsTr("Stop Debugging")
visible: true
}
StepActionImage StepActionImage
{ {
id: runBackAction; id: runBackAction;

60
mix/qml/MainContent.qml

@ -20,13 +20,14 @@ Rectangle {
anchors.fill: parent anchors.fill: parent
id: root id: root
property alias rightViewVisible: rightView.visible property alias rightViewVisible: scenarioExe.visible
property alias webViewVisible: webPreview.visible property alias webViewVisible: webPreview.visible
property alias webView: webPreview property alias webView: webPreview
property alias projectViewVisible: projectList.visible property alias projectViewVisible: projectList.visible
property alias projectNavigator: projectList property alias projectNavigator: projectList
property alias runOnProjectLoad: mainSettings.runOnProjectLoad property alias runOnProjectLoad: mainSettings.runOnProjectLoad
property alias rightPane: rightView property alias rightPane: scenarioExe
property alias debuggerPanel: debugPanel
property alias codeEditor: codeEditor property alias codeEditor: codeEditor
property bool webViewHorizontal: codeWebSplitter.orientation === Qt.Vertical //vertical splitter positions elements vertically, splits screen horizontally property bool webViewHorizontal: codeWebSplitter.orientation === Qt.Vertical //vertical splitter positions elements vertically, splits screen horizontally
property bool firstCompile: true property bool firstCompile: true
@ -43,7 +44,7 @@ Rectangle {
} }
Connections { Connections {
target: rightView target: debugPanel
onDebugExecuteLocation: { onDebugExecuteLocation: {
codeEditor.highlightExecution(documentId, location); codeEditor.highlightExecution(documentId, location);
} }
@ -52,7 +53,7 @@ Rectangle {
Connections { Connections {
target: codeEditor target: codeEditor
onBreakpointsChanged: { onBreakpointsChanged: {
rightPane.setBreakpoints(codeEditor.getBreakpoints()); debugPanel.setBreakpoints(codeEditor.getBreakpoints());
} }
} }
@ -63,20 +64,20 @@ Rectangle {
} }
function toggleRightView() { function toggleRightView() {
rightView.visible = !rightView.visible; scenarioExe.visible = !scenarioExe.visible;
} }
function ensureRightView() { function ensureRightView() {
rightView.visible = true; scenarioExe.visible = true;
} }
function rightViewIsVisible() function rightViewIsVisible()
{ {
return rightView.visible; return scenarioExe.visible;
} }
function hideRightView() { function hideRightView() {
rightView.visible = false; scenarioExe.visible = lfalse;
} }
function toggleWebPreview() { function toggleWebPreview() {
@ -98,8 +99,8 @@ Rectangle {
function displayCompilationErrorIfAny() function displayCompilationErrorIfAny()
{ {
rightView.visible = true; scenarioExe.visible = true;
rightView.displayCompilationErrorIfAny(); scenarioExe.displayCompilationErrorIfAny();
} }
Settings { Settings {
@ -153,7 +154,7 @@ Rectangle {
id: splitSettings id: splitSettings
property alias projectWidth: projectList.width property alias projectWidth: projectList.width
property alias contentViewWidth: contentView.width property alias contentViewWidth: contentView.width
property alias rightViewWidth: rightView.width property alias rightViewWidth: scenarioExe.width
} }
Splitter Splitter
@ -198,14 +199,45 @@ Rectangle {
} }
} }
Debugger { ScenarioExecution
{
id: scenarioExe;
visible: false; visible: false;
id: rightView;
Layout.fillHeight: true Layout.fillHeight: true
Keys.onEscapePressed: visible = false Keys.onEscapePressed: visible = false
Layout.minimumWidth: 515 Layout.minimumWidth: 650
anchors.right: parent.right anchors.right: parent.right
} }
Debugger
{
id: debugPanel
visible: false
Layout.fillHeight: true
Keys.onEscapePressed: visible = false
Layout.minimumWidth: 650
anchors.right: parent.right
}
Connections {
target: clientModel
onDebugDataReady: {
scenarioExe.visible = false
debugPanel.visible = true
if (scenarioExe.bc.debugTrRequested)
{
debugPanel.setTr(scenarioExe.bc.model.blocks[scenarioExe.bc.debugTrRequested[0]].transactions[scenarioExe.bc.debugTrRequested[1]])
}
}
}
Connections {
target: debugPanel
onPanelClosed: {
debugPanel.visible = false
scenarioExe.visible = true
}
}
} }
} }
} }

13
mix/qml/QAddressView.qml

@ -39,15 +39,19 @@ Item
{ {
accountRef.clear(); accountRef.clear();
accountRef.append({"itemid": " - "}); accountRef.append({"itemid": " - "});
if (subType === "contract" || subType === "address") if (subType === "contract" || subType === "address")
{ {
var trCr = 0; var trCr = 0;
for (var k = 0; k < transactionsModel.count; k++) if (blockChainPanel)
for (var k = 0; k < blockChainPanel.model.blocks.length; k++)
{
if (k > blockIndex)
break;
for (var i = 0; i < blockChainPanel.model.blocks[k].transactions.length; i++)
{ {
if (k >= transactionIndex) if (i > transactionIndex)
break; break;
var tr = transactionsModel.get(k); var tr = blockChainPanel.model.blocks[k].transactions[i]
if (tr.functionId === tr.contractId /*&& (dec[1] === tr.contractId || item.subType === "address")*/) if (tr.functionId === tr.contractId /*&& (dec[1] === tr.contractId || item.subType === "address")*/)
{ {
accountRef.append({ "itemid": tr.contractId + " - " + trCr, "value": "<" + tr.contractId + " - " + trCr + ">", "type": "contract" }); accountRef.append({ "itemid": tr.contractId + " - " + trCr, "value": "<" + tr.contractId + " - " + trCr + ">", "type": "contract" });
@ -55,6 +59,7 @@ Item
} }
} }
} }
}
if (subType === "address") if (subType === "address")
{ {
for (k = 0; k < accounts.length; k++) for (k = 0; k < accounts.length; k++)

67
mix/qml/ScenarioButton.qml

@ -0,0 +1,67 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.0
import QtQuick.Controls.Styles 1.1
Rectangle {
id: buttonActionContainer
property string text
property string buttonShortcut
property string sourceImg
property string fillColor
signal clicked
Rectangle {
id: contentRectangle
anchors.fill: parent
border.color: "#cccccc"
border.width: 1
radius: 4
color: parent.fillColor ? parent.fillColor : "white"
Image {
id: debugImage
anchors {
left: parent.left
right: parent.right
top: parent.top
bottom: parent.bottom
bottomMargin: debugImg.pressed ? 0 : 2;
topMargin: debugImg.pressed ? 2 : 0;
}
source: sourceImg
fillMode: Image.PreserveAspectFit
height: 30
}
Button {
anchors.fill: parent
id: debugImg
action: buttonAction
style: ButtonStyle {
background: Rectangle {
color: "transparent"
}
}
}
Action {
id: buttonAction
shortcut: buttonShortcut
onTriggered: {
buttonActionContainer.clicked();
}
}
}
Rectangle
{
anchors.top: contentRectangle.bottom
anchors.topMargin: 15
width: parent.width
Text
{
text: buttonActionContainer.text
anchors.centerIn: parent
}
}
}

57
mix/qml/ScenarioExecution.qml

@ -0,0 +1,57 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.1
import QtQuick.Dialogs 1.1
import QtQuick.Layouts 1.1
import Qt.labs.settings 1.0
import "js/Debugger.js" as Debugger
import "js/ErrorLocationFormater.js" as ErrorLocationFormater
import "."
Rectangle {
color: "#ededed"
property alias bc: blockChain
Connections
{
target: projectModel
onProjectLoaded: {
loader.init()
}
}
Column
{
anchors.margins: 10
anchors.fill: parent
spacing: 10
ScenarioLoader
{
height: 70
width: parent.width
id: loader
}
Rectangle
{
width: parent.width
height: 1
color: "#cccccc"
}
Connections
{
target: loader
onLoaded: {
blockChain.load(scenario)
}
}
BlockChain
{
id: blockChain
width: parent.width
}
}
}

175
mix/qml/ScenarioLoader.qml

@ -0,0 +1,175 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.1
import QtQuick.Layouts 1.1
import QtQuick.Dialogs 1.2
import QtQuick.Window 2.0
import QtQuick.Dialogs 1.1
import Qt.labs.settings 1.0
import "js/Debugger.js" as Debugger
import "js/ErrorLocationFormater.js" as ErrorLocationFormater
import "."
RowLayout
{
signal restored(variant scenario)
signal saved(variant scenario)
signal duplicated(variant scenario)
signal loaded(variant scenario)
spacing: 0
function init()
{
scenarioList.load()
}
id: blockChainSelector
Dialog {
id: newStateWin
modality: Qt.ApplicationModal
title: qsTr("New Project");
width: 320
height: 120
visible: false
contentItem: Rectangle {
anchors.fill: parent
anchors.margins: 10
RowLayout {
anchors.verticalCenter: parent.verticalCenter
Text {
text: qsTr("Name:")
}
Rectangle
{
Layout.preferredWidth: 250
Layout.preferredHeight: parent.height
border.width: 1
border.color: "#cccccc"
TextInput
{
anchors.fill: parent
id: stateName
}
}
}
RowLayout
{
anchors.bottom: parent.bottom
anchors.right: parent.right;
function acceptAndClose()
{
var item = projectModel.stateListModel.createDefaultState();
item.title = stateName.text
projectModel.stateListModel.appendState(item)
projectModel.stateListModel.save()
close()
scenarioList.currentIndex = projectModel.stateListModel.count - 1
}
function close()
{
newStateWin.close()
stateName.text = ""
}
Button {
id: okButton;
enabled: stateName.text !== ""
text: qsTr("OK");
onClicked: {
parent.acceptAndClose();
}
}
Button {
text: qsTr("Cancel");
onClicked: parent.close();
}
}
}
}
ComboBox
{
id: scenarioList
model: projectModel.stateListModel
textRole: "title"
onCurrentIndexChanged:
{
restoreScenario.restore()
}
function load()
{
var state = projectModel.stateListModel.getState(currentIndex)
loaded(state)
}
}
Row
{
Layout.preferredWidth: 100 * 4
Layout.preferredHeight: 30
spacing: 0
ScenarioButton {
id: restoreScenario
width: 100
height: 30
buttonShortcut: ""
sourceImg: "qrc:/qml/img/restoreicon@2x.png"
onClicked: {
restore()
}
text: qsTr("Restore")
function restore()
{
var state = projectModel.stateListModel.reloadStateFromFromProject(scenarioList.currentIndex)
restored(state)
loaded(state)
}
}
ScenarioButton {
id: saveScenario
text: qsTr("Save")
onClicked: {
projectModel.saveProjectFile()
saved(state)
}
width: 100
height: 30
buttonShortcut: ""
sourceImg: "qrc:/qml/img/saveicon@2x.png"
}
ScenarioButton
{
id: duplicateScenario
text: qsTr("Duplicate")
onClicked: {
projectModel.stateListModel.duplicateState(scenarioList.currentIndex)
duplicated(state)
}
width: 100
height: 30
buttonShortcut: ""
sourceImg: "qrc:/qml/img/duplicateicon@2x.png"
}
ScenarioButton {
id: addScenario
width: 100
height: 30
buttonShortcut: ""
sourceImg: "qrc:/qml/img/plus.png"
onClicked: {
newStateWin.open()
}
text: qsTr("New")
}
}
}

117
mix/qml/StateListModel.qml

@ -20,13 +20,29 @@ Item {
s.accounts = [stateListModel.newAccount("1000000", QEther.Ether, defaultAccount)]; //support for old project s.accounts = [stateListModel.newAccount("1000000", QEther.Ether, defaultAccount)]; //support for old project
if (!s.contracts) if (!s.contracts)
s.contracts = []; s.contracts = [];
return {
title: s.title, var ret = {};
transactions: s.transactions.filter(function(t) { return !t.stdContract; }).map(fromPlainTransactionItem), //support old projects by filtering std contracts ret.title = s.title;
accounts: s.accounts.map(fromPlainAccountItem), ret.transactions = s.transactions.filter(function(t) { return !t.stdContract; }).map(fromPlainTransactionItem); //support old projects by filtering std contracts
contracts: s.contracts.map(fromPlainAccountItem), if (s.blocks)
miner: s.miner ret.blocks = s.blocks.map(fromPlainBlockItem);
}; ret.accounts = s.accounts.map(fromPlainAccountItem);
ret.contracts = s.contracts.map(fromPlainAccountItem);
ret.miner = s.miner;
// support old projects
if (!ret.blocks)
{
ret.blocks = [{
hash: "",
number: -1,
transactions: [],
status: "pending"
}]
for (var j in ret.transactions)
ret.blocks[0].transactions.push(fromPlainTransactionItem(toPlainTransactionItem(ret.transactions[j])))
}
return ret;
} }
function fromPlainAccountItem(t) function fromPlainAccountItem(t)
@ -58,9 +74,13 @@ Item {
sender: t.sender, sender: t.sender,
isContractCreation: t.isContractCreation, isContractCreation: t.isContractCreation,
label: t.label, label: t.label,
isFunctionCall: t.isFunctionCall isFunctionCall: t.isFunctionCall,
saveStatus: t.saveStatus
}; };
if (r.saveStatus === undefined)
r.saveStatus = true
if (r.isFunctionCall === undefined) if (r.isFunctionCall === undefined)
r.isFunctionCall = true; r.isFunctionCall = true;
@ -76,9 +96,21 @@ Item {
return r; return r;
} }
function fromPlainBlockItem(b)
{
var r = {
hash: b.hash,
number: b.number,
transactions: b.transactions.filter(function(t) { return !t.stdContract; }).map(fromPlainTransactionItem), //support old projects by filtering std contracts
status: b.status
}
return r;
}
function toPlainStateItem(s) { function toPlainStateItem(s) {
return { return {
title: s.title, title: s.title,
blocks: s.blocks.map(toPlainBlockItem),
transactions: s.transactions.map(toPlainTransactionItem), transactions: s.transactions.map(toPlainTransactionItem),
accounts: s.accounts.map(toPlainAccountItem), accounts: s.accounts.map(toPlainAccountItem),
contracts: s.contracts.map(toPlainAccountItem), contracts: s.contracts.map(toPlainAccountItem),
@ -96,6 +128,17 @@ Item {
return ''; return '';
} }
function toPlainBlockItem(b)
{
var r = {
hash: b.hash,
number: b.number,
transactions: b.transactions.map(toPlainTransactionItem),
status: b.status
}
return r;
}
function toPlainAccountItem(t) function toPlainAccountItem(t)
{ {
return { return {
@ -125,7 +168,8 @@ Item {
parameters: {}, parameters: {},
isContractCreation: t.isContractCreation, isContractCreation: t.isContractCreation,
label: t.label, label: t.label,
isFunctionCall: t.isFunctionCall isFunctionCall: t.isFunctionCall,
saveStatus: t.saveStatus
}; };
for (var key in t.parameters) for (var key in t.parameters)
r.parameters[key] = t.parameters[key]; r.parameters[key] = t.parameters[key];
@ -146,6 +190,8 @@ Item {
projectData.states.push(toPlainStateItem(stateList[i])); projectData.states.push(toPlainStateItem(stateList[i]));
} }
projectData.defaultStateIndex = stateListModel.defaultStateIndex; projectData.defaultStateIndex = stateListModel.defaultStateIndex;
stateListModel.data = projectData
} }
onNewProject: { onNewProject: {
var state = toPlainStateItem(stateListModel.createDefaultState()); var state = toPlainStateItem(stateListModel.createDefaultState());
@ -170,6 +216,11 @@ Item {
id: stateDialog id: stateDialog
onAccepted: { onAccepted: {
var item = stateDialog.getItem(); var item = stateDialog.getItem();
saveState(item);
}
function saveState(item)
{
if (stateDialog.stateIndex < stateListModel.count) { if (stateDialog.stateIndex < stateListModel.count) {
if (stateDialog.isDefault) if (stateDialog.isDefault)
stateListModel.defaultStateIndex = stateIndex; stateListModel.defaultStateIndex = stateIndex;
@ -190,6 +241,7 @@ Item {
ListModel { ListModel {
id: stateListModel id: stateListModel
property int defaultStateIndex: 0 property int defaultStateIndex: 0
property variant data
signal defaultStateChanged; signal defaultStateChanged;
signal stateListModelReady; signal stateListModelReady;
signal stateRun(int index) signal stateRun(int index)
@ -208,12 +260,32 @@ Item {
return { name: name, secret: _secret, balance: QEtherHelper.createEther(_balance, _unit), address: address }; return { name: name, secret: _secret, balance: QEtherHelper.createEther(_balance, _unit), address: address };
} }
function duplicateState(index)
{
var state = stateList[index]
var item = fromPlainStateItem(toPlainStateItem(state))
item.title = qsTr("Copy of") + " " + state.title
appendState(item)
save()
}
function createEmptyBlock()
{
return {
hash: "",
number: -1,
transactions: [],
status: "pending"
}
}
function createDefaultState() { function createDefaultState() {
var item = { var item = {
title: "", title: "",
transactions: [], transactions: [],
accounts: [], accounts: [],
contracts: [] contracts: [],
blocks: [{ status: "pending", number: -1, hash: "", transactions: []}]
}; };
var account = newAccount("1000000", QEther.Ether, defaultAccount) var account = newAccount("1000000", QEther.Ether, defaultAccount)
@ -228,6 +300,7 @@ Item {
ctorTr.label = qsTr("Deploy") + " " + ctorTr.contractId; ctorTr.label = qsTr("Deploy") + " " + ctorTr.contractId;
ctorTr.sender = item.accounts[0].secret; ctorTr.sender = item.accounts[0].secret;
item.transactions.push(ctorTr); item.transactions.push(ctorTr);
item.blocks[0].transactions.push(ctorTr)
} }
return item; return item;
} }
@ -284,10 +357,20 @@ Item {
stateDialog.open(stateListModel.count, item, false); stateDialog.open(stateListModel.count, item, false);
} }
function appendState(item)
{
stateListModel.append(item);
stateList.push(item);
}
function editState(index) { function editState(index) {
stateDialog.open(index, stateList[index], defaultStateIndex === index); stateDialog.open(index, stateList[index], defaultStateIndex === index);
} }
function getState(index) {
return stateList[index];
}
function debugDefaultState() { function debugDefaultState() {
if (defaultStateIndex >= 0 && defaultStateIndex < stateList.length) if (defaultStateIndex >= 0 && defaultStateIndex < stateList.length)
runState(defaultStateIndex); runState(defaultStateIndex);
@ -295,7 +378,7 @@ Item {
function runState(index) { function runState(index) {
var item = stateList[index]; var item = stateList[index];
clientModel.setupState(item); clientModel.setupScenario(item);
stateRun(index); stateRun(index);
} }
@ -322,8 +405,20 @@ Item {
return stateList[defaultStateIndex].title; return stateList[defaultStateIndex].title;
} }
function reloadStateFromFromProject(index)
{
if (data)
{
var item = fromPlainStateItem(data.states[index])
stateListModel.set(index, item)
stateList[index] = item
return item
}
}
function loadStatesFromProject(projectData) function loadStatesFromProject(projectData)
{ {
data = projectData
if (!projectData.states) if (!projectData.states)
projectData.states = []; projectData.states = [];
if (projectData.defaultStateIndex !== undefined) if (projectData.defaultStateIndex !== undefined)

1
mix/qml/StructView.qml

@ -9,6 +9,7 @@ Column
property alias members: repeater.model //js array property alias members: repeater.model //js array
property variant accounts property variant accounts
property var value: ({}) property var value: ({})
property int blockIndex
property int transactionIndex property int transactionIndex
property string context property string context
Layout.fillWidth: true Layout.fillWidth: true

14
mix/qml/TransactionDialog.qml

@ -17,6 +17,7 @@ Dialog {
visible: false visible: false
title: qsTr("Edit Transaction") title: qsTr("Edit Transaction")
property int transactionIndex property int transactionIndex
property int blockIndex
property alias gas: gasValueEdit.gasValue; property alias gas: gasValueEdit.gasValue;
property alias gasAuto: gasAutoCheck.checked; property alias gasAuto: gasAutoCheck.checked;
property alias gasPrice: gasPriceField.value; property alias gasPrice: gasPriceField.value;
@ -27,21 +28,24 @@ Dialog {
property var paramsModel: []; property var paramsModel: [];
property bool useTransactionDefaultValue: false property bool useTransactionDefaultValue: false
property alias stateAccounts: senderComboBox.model property alias stateAccounts: senderComboBox.model
property bool saveStatus
signal accepted; signal accepted;
StateDialogStyle { StateDialogStyle {
id: transactionDialogStyle id: transactionDialogStyle
} }
function open(index, item) { function open(index, blockIdx, item) {
rowFunction.visible = !useTransactionDefaultValue; rowFunction.visible = !useTransactionDefaultValue;
rowValue.visible = !useTransactionDefaultValue; rowValue.visible = !useTransactionDefaultValue;
rowGas.visible = !useTransactionDefaultValue; rowGas.visible = !useTransactionDefaultValue;
rowGasPrice.visible = !useTransactionDefaultValue; rowGasPrice.visible = !useTransactionDefaultValue;
transactionIndex = index; transactionIndex = index
typeLoader.transactionIndex = index; blockIndex = blockIdx
typeLoader.transactionIndex = index
typeLoader.blockIndex = blockIdx
saveStatus = item.saveStatus
gasValueEdit.gasValue = item.gas; gasValueEdit.gasValue = item.gas;
gasAutoCheck.checked = item.gasAuto ? true : false; gasAutoCheck.checked = item.gasAuto ? true : false;
gasPriceField.value = item.gasPrice; gasPriceField.value = item.gasPrice;
@ -229,7 +233,7 @@ Dialog {
item.functionId = item.contractId; item.functionId = item.contractId;
item.label = qsTr("Deploy") + " " + item.contractId; item.label = qsTr("Deploy") + " " + item.contractId;
} }
item.saveStatus = saveStatus
item.sender = senderComboBox.model[senderComboBox.currentIndex].secret; item.sender = senderComboBox.model[senderComboBox.currentIndex].secret;
item.parameters = paramValues; item.parameters = paramValues;
return item; return item;

BIN
mix/qml/img/newIcon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 801 B

BIN
mix/qml/img/newIcon@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

2
mix/qml/js/ProjectModel.js

@ -101,6 +101,8 @@ function loadProject(path) {
console.log("Loading project at " + path); console.log("Loading project at " + path);
var projectFile = path + projectFileName; var projectFile = path + projectFileName;
var json = fileIo.readFile(projectFile); var json = fileIo.readFile(projectFile);
if (!json)
return;
var projectData = JSON.parse(json); var projectData = JSON.parse(json);
if (projectData.deploymentDir) if (projectData.deploymentDir)
projectModel.deploymentDir = projectData.deploymentDir projectModel.deploymentDir = projectData.deploymentDir

3
mix/qml/js/TransactionHelper.js

@ -12,7 +12,8 @@ function defaultTransaction()
stdContract: false, stdContract: false,
isContractCreation: true, isContractCreation: true,
label: "", label: "",
isFunctionCall: true isFunctionCall: true,
saveStatus: true
}; };
} }

22
mix/res.qrc

@ -68,5 +68,27 @@
<file>qml/img/warningicon.png</file> <file>qml/img/warningicon.png</file>
<file>qml/img/warningicon@2x.png</file> <file>qml/img/warningicon@2x.png</file>
<file>qml/QAddressView.qml</file> <file>qml/QAddressView.qml</file>
<file>qml/img/addblock.png</file>
<file>qml/img/addblock@2x.png</file>
<file>qml/img/duplicateicon.png</file>
<file>qml/img/duplicateicon@2x.png</file>
<file>qml/img/leftarrow.png</file>
<file>qml/img/leftarrow@2x.png</file>
<file>qml/img/newaccounticon.png</file>
<file>qml/img/newaccounticon@2x.png</file>
<file>qml/img/recyclediscard.png</file>
<file>qml/img/recyclediscard@2x.png</file>
<file>qml/img/recycleicon.png</file>
<file>qml/img/recycleicon@2x.png</file>
<file>qml/img/recyclekeep.png</file>
<file>qml/img/recyclekeep@2x.png</file>
<file>qml/img/restoreicon.png</file>
<file>qml/img/restoreicon@2x.png</file>
<file>qml/img/rightarrow.png</file>
<file>qml/img/rightarrow@2x.png</file>
<file>qml/img/saveicon.png</file>
<file>qml/img/saveicon@2x.png</file>
<file>qml/img/sendtransactionicon.png</file>
<file>qml/img/sendtransactionicon@2x.png</file>
</qresource> </qresource>
</RCC> </RCC>

2
test/TestHelper.cpp

@ -352,6 +352,8 @@ void ImportTest::exportTest(bytes const& _output, State const& _statePost)
BOOST_CHECK_MESSAGE((m_TestObject["out"].get_str() == m_TestObject["expectOut"].get_str()), warning); BOOST_CHECK_MESSAGE((m_TestObject["out"].get_str() == m_TestObject["expectOut"].get_str()), warning);
else else
BOOST_WARN_MESSAGE((m_TestObject["out"].get_str() == m_TestObject["expectOut"].get_str()), warning); BOOST_WARN_MESSAGE((m_TestObject["out"].get_str() == m_TestObject["expectOut"].get_str()), warning);
m_TestObject.erase(m_TestObject.find("expectOut"));
} }
// export logs // export logs

13
test/libevm/vm.cpp

@ -388,6 +388,19 @@ void doVMTests(json_spirit::mValue& v, bool _fillin)
o["callcreates"] = fev.exportCallCreates(); o["callcreates"] = fev.exportCallCreates();
o["out"] = output.size() > 4096 ? "#" + toString(output.size()) : toHex(output, 2, HexPrefix::Add); o["out"] = output.size() > 4096 ? "#" + toString(output.size()) : toHex(output, 2, HexPrefix::Add);
// compare expected output with post output
if (o.count("expectOut") > 0)
{
std::string warning = "Check State: Error! Unexpected output: " + o["out"].get_str() + " Expected: " + o["expectOut"].get_str();
if (Options::get().checkState)
BOOST_CHECK_MESSAGE((o["out"].get_str() == o["expectOut"].get_str()), warning);
else
BOOST_WARN_MESSAGE((o["out"].get_str() == o["expectOut"].get_str()), warning);
o.erase(o.find("expectOut"));
}
o["gas"] = toCompactHex(fev.gas, HexPrefix::Add, 1); o["gas"] = toCompactHex(fev.gas, HexPrefix::Add, 1);
o["logs"] = exportLog(fev.sub.logs); o["logs"] = exportLog(fev.sub.logs);
} }

8
test/libsolidity/SolidityOptimizer.cpp

@ -944,6 +944,14 @@ BOOST_AUTO_TEST_CASE(cse_access_previous_sequence)
// 0, SLOAD, 1, ADD, SSTORE, 0 SLOAD // 0, SLOAD, 1, ADD, SSTORE, 0 SLOAD
} }
BOOST_AUTO_TEST_CASE(cse_optimise_return)
{
checkCSE(
AssemblyItems{u256(0), u256(7), Instruction::RETURN},
AssemblyItems{Instruction::STOP}
);
}
BOOST_AUTO_TEST_CASE(control_flow_graph_remove_unused) BOOST_AUTO_TEST_CASE(control_flow_graph_remove_unused)
{ {
// remove parts of the code that are unused // remove parts of the code that are unused

247
test/libweb3jsonrpc/webthreestubclient.h

@ -546,6 +546,16 @@ class WebThreeStubClient : public jsonrpc::Client
else else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
} }
bool eth_notePassword(const std::string& param1) throw (jsonrpc::JsonRpcException)
{
Json::Value p;
p.append(param1);
Json::Value result = this->CallMethod("eth_notePassword",p);
if (result.isBool())
return result.asBool();
else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
}
bool db_put(const std::string& param1, const std::string& param2, const std::string& param3) throw (jsonrpc::JsonRpcException) bool db_put(const std::string& param1, const std::string& param2, const std::string& param3) throw (jsonrpc::JsonRpcException)
{ {
Json::Value p; Json::Value p;
@ -661,6 +671,243 @@ class WebThreeStubClient : public jsonrpc::Client
else else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
} }
bool admin_web3_setVerbosity(int param1, const std::string& param2) throw (jsonrpc::JsonRpcException)
{
Json::Value p;
p.append(param1);
p.append(param2);
Json::Value result = this->CallMethod("admin_web3_setVerbosity",p);
if (result.isBool())
return result.asBool();
else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
}
bool admin_net_start(const std::string& param1) throw (jsonrpc::JsonRpcException)
{
Json::Value p;
p.append(param1);
Json::Value result = this->CallMethod("admin_net_start",p);
if (result.isBool())
return result.asBool();
else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
}
bool admin_net_stop(const std::string& param1) throw (jsonrpc::JsonRpcException)
{
Json::Value p;
p.append(param1);
Json::Value result = this->CallMethod("admin_net_stop",p);
if (result.isBool())
return result.asBool();
else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
}
bool admin_net_connect(const std::string& param1, const std::string& param2) throw (jsonrpc::JsonRpcException)
{
Json::Value p;
p.append(param1);
p.append(param2);
Json::Value result = this->CallMethod("admin_net_connect",p);
if (result.isBool())
return result.asBool();
else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
}
Json::Value admin_net_peers(const std::string& param1) throw (jsonrpc::JsonRpcException)
{
Json::Value p;
p.append(param1);
Json::Value result = this->CallMethod("admin_net_peers",p);
if (result.isArray())
return result;
else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
}
Json::Value admin_eth_blockQueueStatus(const std::string& param1) throw (jsonrpc::JsonRpcException)
{
Json::Value p;
p.append(param1);
Json::Value result = this->CallMethod("admin_eth_blockQueueStatus",p);
if (result.isObject())
return result;
else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
}
bool admin_eth_setAskPrice(const std::string& param1, const std::string& param2) throw (jsonrpc::JsonRpcException)
{
Json::Value p;
p.append(param1);
p.append(param2);
Json::Value result = this->CallMethod("admin_eth_setAskPrice",p);
if (result.isBool())
return result.asBool();
else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
}
bool admin_eth_setBidPrice(const std::string& param1, const std::string& param2) throw (jsonrpc::JsonRpcException)
{
Json::Value p;
p.append(param1);
p.append(param2);
Json::Value result = this->CallMethod("admin_eth_setBidPrice",p);
if (result.isBool())
return result.asBool();
else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
}
bool admin_eth_setReferencePrice(const std::string& param1, const std::string& param2) throw (jsonrpc::JsonRpcException)
{
Json::Value p;
p.append(param1);
p.append(param2);
Json::Value result = this->CallMethod("admin_eth_setReferencePrice",p);
if (result.isBool())
return result.asBool();
else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
}
bool admin_eth_setPriority(int param1, const std::string& param2) throw (jsonrpc::JsonRpcException)
{
Json::Value p;
p.append(param1);
p.append(param2);
Json::Value result = this->CallMethod("admin_eth_setPriority",p);
if (result.isBool())
return result.asBool();
else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
}
bool admin_eth_setMining(bool param1, const std::string& param2) throw (jsonrpc::JsonRpcException)
{
Json::Value p;
p.append(param1);
p.append(param2);
Json::Value result = this->CallMethod("admin_eth_setMining",p);
if (result.isBool())
return result.asBool();
else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
}
Json::Value admin_eth_findBlock(const std::string& param1, const std::string& param2) throw (jsonrpc::JsonRpcException)
{
Json::Value p;
p.append(param1);
p.append(param2);
Json::Value result = this->CallMethod("admin_eth_findBlock",p);
if (result.isObject())
return result;
else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
}
std::string admin_eth_blockQueueFirstUnknown(const std::string& param1) throw (jsonrpc::JsonRpcException)
{
Json::Value p;
p.append(param1);
Json::Value result = this->CallMethod("admin_eth_blockQueueFirstUnknown",p);
if (result.isString())
return result.asString();
else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
}
bool admin_eth_blockQueueRetryUnknown(const std::string& param1) throw (jsonrpc::JsonRpcException)
{
Json::Value p;
p.append(param1);
Json::Value result = this->CallMethod("admin_eth_blockQueueRetryUnknown",p);
if (result.isBool())
return result.asBool();
else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
}
Json::Value admin_eth_allAccounts(const std::string& param1) throw (jsonrpc::JsonRpcException)
{
Json::Value p;
p.append(param1);
Json::Value result = this->CallMethod("admin_eth_allAccounts",p);
if (result.isArray())
return result;
else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
}
Json::Value admin_eth_newAccount(const Json::Value& param1, const std::string& param2) throw (jsonrpc::JsonRpcException)
{
Json::Value p;
p.append(param1);
p.append(param2);
Json::Value result = this->CallMethod("admin_eth_newAccount",p);
if (result.isObject())
return result;
else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
}
bool admin_eth_setSigningKey(const std::string& param1, const std::string& param2) throw (jsonrpc::JsonRpcException)
{
Json::Value p;
p.append(param1);
p.append(param2);
Json::Value result = this->CallMethod("admin_eth_setSigningKey",p);
if (result.isBool())
return result.asBool();
else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
}
bool admin_eth_setMiningBenefactor(const std::string& param1, const std::string& param2) throw (jsonrpc::JsonRpcException)
{
Json::Value p;
p.append(param1);
p.append(param2);
Json::Value result = this->CallMethod("admin_eth_setMiningBenefactor",p);
if (result.isBool())
return result.asBool();
else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
}
Json::Value admin_eth_inspect(const std::string& param1, const std::string& param2) throw (jsonrpc::JsonRpcException)
{
Json::Value p;
p.append(param1);
p.append(param2);
Json::Value result = this->CallMethod("admin_eth_inspect",p);
if (result.isObject())
return result;
else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
}
Json::Value admin_eth_reprocess(const std::string& param1, const std::string& param2) throw (jsonrpc::JsonRpcException)
{
Json::Value p;
p.append(param1);
p.append(param2);
Json::Value result = this->CallMethod("admin_eth_reprocess",p);
if (result.isObject())
return result;
else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
}
Json::Value admin_eth_vmTrace(const std::string& param1, const std::string& param2, const std::string& param3) throw (jsonrpc::JsonRpcException)
{
Json::Value p;
p.append(param1);
p.append(param2);
p.append(param3);
Json::Value result = this->CallMethod("admin_eth_vmTrace",p);
if (result.isObject())
return result;
else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
}
Json::Value admin_eth_getReceiptByHashAndIndex(const std::string& param1, const std::string& param2, const std::string& param3) throw (jsonrpc::JsonRpcException)
{
Json::Value p;
p.append(param1);
p.append(param2);
p.append(param3);
Json::Value result = this->CallMethod("admin_eth_getReceiptByHashAndIndex",p);
if (result.isObject())
return result;
else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
}
}; };
#endif //JSONRPC_CPP_STUB_WEBTHREESTUBCLIENT_H_ #endif //JSONRPC_CPP_STUB_WEBTHREESTUBCLIENT_H_

Loading…
Cancel
Save